diff --git a/Directory.Build.props b/Directory.Build.props index 4f823ab..8a7a99c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 1.6.2 - 1.6.0.2 + 1.6.3 + 1.6.0.3 1.6.0.2 https://avionblock.github.io/OpusSharp/index.html https://github.com/AvionBlock/OpusSharp diff --git a/OpusSharp.Core/Dynamic/OpusDecoder.cs b/OpusSharp.Core/Dynamic/OpusDecoder.cs new file mode 100644 index 0000000..38d7432 --- /dev/null +++ b/OpusSharp.Core/Dynamic/OpusDecoder.cs @@ -0,0 +1,271 @@ +using System; +using OpusSharp.Core.Interfaces; +using OpusSharp.Core.SafeHandlers; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +// ReSharper disable ClassNeverInstantiated.Global +namespace OpusSharp.Core.Dynamic +{ + /// + /// An opus decoder using dynamic binding calls. + /// + public class OpusDecoder : IOpusDecoder + { + /// + /// Direct safe handle for the . IT IS NOT RECOMMENDED TO CLOSE THE HANDLE DIRECTLY! Instead, use to dispose the handle and object safely. + /// + protected OpusDecoderSafeHandle _handler; + + private bool _disposed; + + /// + /// Creates a new opus decoder. + /// + /// The sample rate, this must be one of 8000, 12000, 16000, 24000, or 48000. + /// Number of channels, this must be 1 or 2. + /// + public unsafe OpusDecoder(int sample_rate, int channels) + { + var error = 0; + _handler = NativeOpus.opus_decoder_create(sample_rate, channels, &error); + CheckError(error); + } + + /// + /// Opus decoder destructor. + /// + ~OpusDecoder() + { + Dispose(false); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + +#if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER + /// + public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_decode(_handler, inputPtr, length, (short*)outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (short* outputPtr = output) + { + var result = NativeOpus.opus_decode(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (int* outputPtr = output) + { + var result = NativeOpus.opus_decode24(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (float* outputPtr = output) + { + var result = NativeOpus.opus_decode_float(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } +#endif + + /// + public unsafe int Decode(byte[]? input, int length, byte[] output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_decode(_handler, inputPtr, length, (short*)outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(byte[]? input, int length, short[] output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (short* outputPtr = output) + { + var result = NativeOpus.opus_decode(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(byte[]? input, int length, int[] output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (int* outputPtr = output) + { + var result = NativeOpus.opus_decode24(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(byte[]? input, int length, float[] output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (float* outputPtr = output) + { + var result = NativeOpus.opus_decode_float(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public int Ctl(DecoderCTL request) + { + ThrowIfDisposed(); + var result = NativeOpus.opus_decoder_ctl(_handler, (int)request); + CheckError(result); + return result; + } + + /// + public int Ctl(DecoderCTL request, int value) + { + ThrowIfDisposed(); + var result = NativeOpus.opus_decoder_ctl(_handler, (int)request, value); + CheckError(result); + return result; + } + + /// + public unsafe int Ctl(DecoderCTL request, ref T value) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = NativeOpus.opus_decoder_ctl(_handler, (int)request, valuePtr); + CheckError(result); + return result; + } + } + + /// + public int Ctl(GenericCTL request) + { + ThrowIfDisposed(); + var result = NativeOpus.opus_decoder_ctl(_handler, (int)request); + CheckError(result); + return result; + } + + /// + public int Ctl(GenericCTL request, int value) + { + ThrowIfDisposed(); + var result = NativeOpus.opus_decoder_ctl(_handler, (int)request, value); + CheckError(result); + return result; + } + + /// + public unsafe int Ctl(GenericCTL request, ref T value) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = NativeOpus.opus_decoder_ctl(_handler, (int)request, valuePtr); + CheckError(result); + return result; + } + } + + /// + /// Dispose logic. + /// + /// Set to true if fully disposing. + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + + if (disposing) + { + if (!_handler.IsClosed) + _handler.Close(); + } + + _disposed = true; + } + + /// + /// Throws an exception if this object is disposed or the handler is closed. + /// + /// + protected virtual void ThrowIfDisposed() + { + if (_disposed || _handler.IsClosed) + throw new ObjectDisposedException(GetType().FullName); + } + + /// + /// Checks if there is an opus error and throws if the error is a negative value. + /// + /// The error code to input. + /// + protected static void CheckError(int error) + { + if (error < 0) + throw new OpusException(((OpusErrorCodes)error).ToString()); + } + } +} \ No newline at end of file diff --git a/OpusSharp.Core/Dynamic/OpusEncoder.cs b/OpusSharp.Core/Dynamic/OpusEncoder.cs new file mode 100644 index 0000000..6ac7ef8 --- /dev/null +++ b/OpusSharp.Core/Dynamic/OpusEncoder.cs @@ -0,0 +1,299 @@ +using OpusSharp.Core.SafeHandlers; +using System; +using OpusSharp.Core.Interfaces; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +namespace OpusSharp.Core.Dynamic +{ + /// + /// An opus encoder using dynamic binding calls. + /// + public class OpusEncoder : IOpusEncoder + { + /// + /// Direct safe handle for the . IT IS NOT RECOMMENDED TO CLOSE THE HANDLE DIRECTLY! Instead, use to dispose the handle and object safely. + /// + protected OpusEncoderSafeHandle _handler; + + private bool _disposed; + + /// + /// Creates a new opus encoder. + /// + /// The sample rate, this must be one of 8000, 12000, 16000, 24000, or 48000. + /// Number of channels, this must be 1 or 2. + /// Coding mode (one of , or + /// + public unsafe OpusEncoder(int sample_rate, int channels, OpusPredefinedValues application) + { + var error = 0; + _handler = NativeOpus.opus_encoder_create(sample_rate, channels, (int)application, &error); + CheckError(error); + } + + /// + /// Opus encoder destructor. + /// + ~OpusEncoder() + { + Dispose(false); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + +#if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER + /// + public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (byte* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_encode(_handler, (short*)inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (short* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_encode(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (int* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_encode24(_handler, inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (float* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_encode_float(_handler, inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } +#endif + + /// + public unsafe int Encode(byte[] input, int frame_size, byte[] output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (byte* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_encode(_handler, (short*)inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(short[] input, int frame_size, byte[] output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (short* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_encode(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(int[] input, int frame_size, byte[] output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (int* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_encode24(_handler, inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(float[] input, int frame_size, byte[] output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (float* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = NativeOpus.opus_encode_float(_handler, inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public int Ctl(EncoderCTL request) + { + ThrowIfDisposed(); + var result = NativeOpus.opus_encoder_ctl(_handler, (int)request); + CheckError(result); + return result; + } + + /// + public int Ctl(EncoderCTL request, int value) + { + ThrowIfDisposed(); + var result = NativeOpus.opus_encoder_ctl(_handler, (int)request, value); + CheckError(result); + return result; + } + + /// + public unsafe int Ctl(EncoderCTL request, ref T value) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = NativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr); + CheckError(result); + return result; + } + } + + /// + public unsafe int Ctl(EncoderCTL request, ref T value, int value2) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = NativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr, value2); + CheckError(result); + return result; + } + } + + /// + public unsafe int Ctl(EncoderCTL request, int value, ref T value2) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* value2Ptr = &value2) + { + var result = NativeOpus.opus_encoder_ctl(_handler, (int)request, value, value2Ptr); + CheckError(result); + return result; + } + } + + /// + public unsafe int Ctl(EncoderCTL request, ref T value, ref T2 value2) + where T : unmanaged where T2 : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + fixed (void* value2Ptr = &value2) + { + var result = NativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr, value2Ptr); + CheckError(result); + return result; + } + } + + /// + public int Ctl(GenericCTL request) + { + ThrowIfDisposed(); + var result = NativeOpus.opus_encoder_ctl(_handler, (int)request); + CheckError(result); + return result; + } + + /// + public int Ctl(GenericCTL request, int value) + { + ThrowIfDisposed(); + var result = NativeOpus.opus_encoder_ctl(_handler, (int)request, value); + CheckError(result); + return result; + } + + /// + public unsafe int Ctl(GenericCTL request, ref T value) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = NativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr); + CheckError(result); + return result; + } + } + + /// + /// Dispose logic. + /// + /// Set to true if fully disposing. + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + + if (disposing) + { + if (!_handler.IsClosed) + _handler.Close(); + } + + _disposed = true; + } + + /// + /// Throws an exception if this object is disposed or the handler is closed. + /// + /// + protected virtual void ThrowIfDisposed() + { + if (_disposed || _handler.IsClosed) + throw new ObjectDisposedException(GetType().FullName); + } + + /// + /// Checks if there is an opus error and throws if the error is a negative value. + /// + /// The error code to input. + /// + protected static void CheckError(int error) + { + if (error < 0) + throw new OpusException(((OpusErrorCodes)error).ToString()); + } + } +} \ No newline at end of file diff --git a/OpusSharp.Core/Dynamic/OpusInfo.cs b/OpusSharp.Core/Dynamic/OpusInfo.cs new file mode 100644 index 0000000..c3c0626 --- /dev/null +++ b/OpusSharp.Core/Dynamic/OpusInfo.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.InteropServices; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +namespace OpusSharp.Core.Dynamic +{ + /// + /// Provides information about the opus DLL. + /// + public class OpusInfo + { + /// + /// Gets the libopus version string. + /// + /// Version string. + public static unsafe string Version() + { + var version = NativeOpus.opus_get_version_string(); + return Marshal.PtrToStringAnsi((IntPtr)version) ?? ""; + } + + /// + /// Converts an opus error code into a human-readable string. + /// + /// Error number. + /// Error string. + public static unsafe string StringError(int error) + { + var stringError = NativeOpus.opus_strerror(error); + return Marshal.PtrToStringAnsi((IntPtr)stringError) ?? ""; + } + } +} diff --git a/OpusSharp.Core/Interfaces/IOpusDecoder.cs b/OpusSharp.Core/Interfaces/IOpusDecoder.cs new file mode 100644 index 0000000..2fd73ce --- /dev/null +++ b/OpusSharp.Core/Interfaces/IOpusDecoder.cs @@ -0,0 +1,180 @@ +using System; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +// ReSharper disable ClassNeverInstantiated.Global +namespace OpusSharp.Core.Interfaces +{ + /// + /// An opus decoder interface. + /// + public interface IOpusDecoder : IDisposable + { +#if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER + /// + /// Decodes an opus encoded frame. + /// + /// Input payload. Use null to indicate packet loss + /// Number of bytes in payload. + /// Output signal (interleaved if 2 channels). length is frame_size*channels*sizeof(short). + /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. + /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. + /// Number of decoded samples or . + /// + /// + int Decode(Span input, int length, Span output, int frame_size, bool decode_fec); + + /// + /// Decodes an opus encoded frame. + /// + /// Input payload. Use null to indicate packet loss + /// Number of bytes in payload. + /// Output signal (interleaved if 2 channels). length is frame_size*channels. + /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. + /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. + /// Number of decoded samples or . + /// + /// + int Decode(Span input, int length, Span output, int frame_size, bool decode_fec); + + /// + /// Decodes an opus encoded frame. + /// + /// Input payload. Use null to indicate packet loss + /// Number of bytes in payload. + /// Output signal (interleaved if 2 channels). length is frame_size*channels. + /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. + /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. + /// Number of decoded samples or . + /// + /// + int Decode(Span input, int length, Span output, int frame_size, bool decode_fec); + + /// + /// Decodes an opus encoded frame. + /// + /// Input payload. Use null to indicate packet loss + /// Number of bytes in payload. + /// Output signal (interleaved if 2 channels). length is (frame_size*channels)/2. Note: I don't know if this is correct. + /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. + /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. + /// Number of decoded samples or . + /// + /// + int Decode(Span input, int length, Span output, int frame_size, bool decode_fec); +#endif + + /// + /// Decodes an opus encoded frame. + /// + /// Input payload. Use null to indicate packet loss + /// Number of bytes in payload. + /// Output signal (interleaved if 2 channels). length is frame_size*channels*sizeof(short). + /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. + /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. + /// Number of decoded samples or . + /// + /// + int Decode(byte[]? input, int length, byte[] output, int frame_size, bool decode_fec); + + /// + /// Decodes an opus encoded frame. + /// + /// Input payload. Use null to indicate packet loss + /// Number of bytes in payload. + /// Output signal (interleaved if 2 channels). length is frame_size*channels. + /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. + /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. + /// Number of decoded samples or . + /// + /// + int Decode(byte[]? input, int length, short[] output, int frame_size, bool decode_fec); + + /// + /// Decodes an opus encoded frame. + /// + /// Input payload. Use null to indicate packet loss + /// Number of bytes in payload. + /// Output signal (interleaved if 2 channels). length is frame_size*channels. + /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. + /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. + /// Number of decoded samples or . + /// + /// + int Decode(byte[]? input, int length, int[] output, int frame_size, bool decode_fec); + + /// + /// Decodes an opus encoded frame. + /// + /// Input payload. Use null to indicate packet loss + /// Number of bytes in payload. + /// Output signal (interleaved if 2 channels). length is (frame_size*channels)/2. Note: I don't know if this is correct. + /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. + /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. + /// Number of decoded samples or . + /// + /// + int Decode(byte[]? input, int length, float[] output, int frame_size, bool decode_fec); + + /// + /// Performs a ctl request. + /// + /// The request you want to specify. + /// The result code of the request. See . + /// + /// + int Ctl(DecoderCTL request); + + /// + /// Performs a ctl set request. + /// + /// The request you want to specify. + /// The input value. + /// The result code of the request. See . + /// + /// + int Ctl(DecoderCTL request, int value); + + /// + /// Performs a ctl get/set request. + /// + /// The type you want to input/output. + /// The request you want to specify. + /// The input/output value. + /// The result code of the request. See . + /// + /// + int Ctl(DecoderCTL request, ref T value) where T : unmanaged; + + /// + /// Performs a ctl request. + /// + /// The request you want to specify. + /// The result code of the request. See . + /// + /// + int Ctl(GenericCTL request); + + /// + /// Performs a ctl set request. + /// + /// The request you want to specify. + /// The input value. + /// The result code of the request. See . + /// + /// + int Ctl(GenericCTL request, int value); + + /// + /// Performs a ctl request. + /// + /// The type you want to input/output. + /// The request you want to specify. + /// The input/output value. + /// The result code of the request. See . + /// + /// + int Ctl(GenericCTL request, ref T value) where T : unmanaged; + } +} \ No newline at end of file diff --git a/OpusSharp.Core/Interfaces/IOpusEncoder.cs b/OpusSharp.Core/Interfaces/IOpusEncoder.cs new file mode 100644 index 0000000..f8cab41 --- /dev/null +++ b/OpusSharp.Core/Interfaces/IOpusEncoder.cs @@ -0,0 +1,209 @@ +using System; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +// ReSharper disable ClassNeverInstantiated.Global +namespace OpusSharp.Core.Interfaces +{ + /// + /// An opus encoder interface. + /// + public interface IOpusEncoder : IDisposable + { +#if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER + /// + /// Encodes a pcm frame. + /// + /// Input signal (interleaved if 2 channels). length is frame_size*channels. + /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. + /// Output payload. This must contain storage for at least max_data_bytes. + /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. + /// The length of the encoded packet (in bytes). + /// + /// + int Encode(Span input, int frame_size, Span output, int max_data_bytes); + + /// + /// Encodes a pcm frame. + /// + /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(short). + /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. + /// Output payload. This must contain storage for at least max_data_bytes. + /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. + /// The length of the encoded packet (in bytes). + /// + /// + int Encode(Span input, int frame_size, Span output, int max_data_bytes); + + /// + /// Encodes a pcm frame. + /// + /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(int). + /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. + /// Output payload. This must contain storage for at least max_data_bytes. + /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. + /// The length of the encoded packet (in bytes). + /// + /// + int Encode(Span input, int frame_size, Span output, int max_data_bytes); + + /// + /// Encodes a floating point pcm frame. + /// + /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(float). + /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. + /// Output payload. This must contain storage for at least max_data_bytes. + /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. + /// The length of the encoded packet (in bytes). + /// + /// + int Encode(Span input, int frame_size, Span output, int max_data_bytes); +#endif + + /// + /// Encodes a pcm frame. + /// + /// Input signal (interleaved if 2 channels). length is frame_size*channels. + /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. + /// Output payload. This must contain storage for at least max_data_bytes. + /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. + /// The length of the encoded packet (in bytes). + /// + /// + int Encode(byte[] input, int frame_size, byte[] output, int max_data_bytes); + + /// + /// Encodes a pcm frame. + /// + /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(short). + /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. + /// Output payload. This must contain storage for at least max_data_bytes. + /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. + /// The length of the encoded packet (in bytes). + /// + /// + int Encode(short[] input, int frame_size, byte[] output, int max_data_bytes); + + /// + /// Encodes a pcm frame. + /// + /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(int). + /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. + /// Output payload. This must contain storage for at least max_data_bytes. + /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. + /// The length of the encoded packet (in bytes). + /// + /// + int Encode(int[] input, int frame_size, byte[] output, int max_data_bytes); + + /// + /// Encodes a floating point pcm frame. + /// + /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(float). + /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. + /// Output payload. This must contain storage for at least max_data_bytes. + /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. + /// The length of the encoded packet (in bytes). + /// + /// + int Encode(float[] input, int frame_size, byte[] output, int max_data_bytes); + + /// + /// Performs a ctl request. + /// + /// The request you want to specify. + /// The result code of the request. See . + /// + /// + int Ctl(EncoderCTL request); + + /// + /// Performs a ctl request. + /// + /// The request you want to specify. + /// /// The input value. + /// The result code of the request. See . + /// + /// + int Ctl(EncoderCTL request, int value); + + /// + /// Performs a ctl request. + /// + /// The type you want to input/output. + /// The request you want to specify. + /// The input/output value. + /// The result code of the request. See . + /// + /// + int Ctl(EncoderCTL request, ref T value) where T : unmanaged; + + /// + /// Performs a ctl request. + /// + /// The type you want to input/output. + /// The request you want to specify. + /// The input/output value. + /// The second input value. + /// The result code of the request. See . + /// + /// + int Ctl(EncoderCTL request, ref T value, int value2) where T : unmanaged; + + /// + /// Performs a ctl request. + /// + /// The type you want to input/output. + /// The request you want to specify. + /// The input value. + /// The second input/output value. + /// The result code of the request. See . + /// + /// + int Ctl(EncoderCTL request, int value, ref T value2) where T : unmanaged; + + /// + /// Performs a ctl request. + /// + /// The type you want to input/output. + /// The second type you want to input/output. + /// The request you want to specify. + /// The input/output value. + /// The second input/output value. + /// The result code of the request. See . + /// + /// + int Ctl(EncoderCTL request, ref T value, ref T2 value2) where T : unmanaged where T2 : unmanaged; + + /// + /// Performs a ctl request. + /// + /// The request you want to specify. + /// The result code of the request. See . + /// + /// + int Ctl(GenericCTL request); + + /// + /// Performs a ctl set request. + /// + /// The request you want to specify. + /// The input value. + /// The result code of the request. See . + /// + /// + int Ctl(GenericCTL request, int value); + + /// + /// Performs a ctl request. + /// + /// The type you want to input/output. + /// The request you want to specify. + /// The input/output value. + /// The result code of the request. See . + /// + /// + int Ctl(GenericCTL request, ref T value) where T : unmanaged; + } +} \ No newline at end of file diff --git a/OpusSharp.Core/OpusDecoder.cs b/OpusSharp.Core/OpusDecoder.cs index 2ff76c5..a9fd4d8 100644 --- a/OpusSharp.Core/OpusDecoder.cs +++ b/OpusSharp.Core/OpusDecoder.cs @@ -1,5 +1,5 @@ -using OpusSharp.Core.SafeHandlers; -using System; +using System; +using OpusSharp.Core.Interfaces; // ReSharper disable MemberCanBePrivate.Global // ReSharper disable FieldCanBeMadeReadOnly.Global @@ -10,15 +10,12 @@ namespace OpusSharp.Core /// /// An opus decoder. /// - public class OpusDecoder : IDisposable + public class OpusDecoder : IOpusDecoder { /// - /// Direct safe handle for the . IT IS NOT RECOMMENDED TO CLOSE THE HANDLE DIRECTLY! Instead, use to dispose the handle and object safely. + /// Direct opus decoder for the . You can close this directly. /// - protected OpusDecoderSafeHandle _handler; - - private readonly bool _useStatic; - private bool _disposed; + protected IOpusDecoder _decoder; /// /// Creates a new opus decoder. @@ -27,407 +24,105 @@ public class OpusDecoder : IDisposable /// Number of channels, this must be 1 or 2. /// Set to to force static imports, to force dynamic imports, or to auto-select based on platform. /// - public unsafe OpusDecoder(int sample_rate, int channels, bool? use_static = null) + public OpusDecoder(int sample_rate, int channels, bool? use_static = null) { - _useStatic = OpusRuntime.ShouldUseStaticImports(use_static); - var error = 0; - _handler = _useStatic - ? StaticNativeOpus.opus_decoder_create(sample_rate, channels, &error) - : NativeOpus.opus_decoder_create(sample_rate, channels, &error); - CheckError(error); + var useStatic = OpusRuntime.ShouldUseStaticImports(use_static); + _decoder = useStatic + ? (IOpusDecoder) new Static.OpusDecoder(sample_rate, channels) + : new Dynamic.OpusDecoder(sample_rate, channels); } - - /// - /// Opus decoder destructor. - /// - ~OpusDecoder() + + /// + public void Dispose() { - Dispose(false); + _decoder.Dispose(); + GC.SuppressFinalize(this); } #if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER - /// - /// Decodes an opus encoded frame. - /// - /// Input payload. Use null to indicate packet loss - /// Number of bytes in payload. - /// Output signal (interleaved if 2 channels). length is frame_size*channels*sizeof(short). - /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. - /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. - /// Number of decoded samples or . - /// - /// - public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + /// + public int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) { - ThrowIfDisposed(); - - fixed (byte* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_decode(_handler, inputPtr, length, (short*)outputPtr, frame_size, - decode_fec ? 1 : 0) - : NativeOpus.opus_decode(_handler, inputPtr, length, (short*)outputPtr, frame_size, - decode_fec ? 1 : 0); - CheckError(result); - return result; - } + return _decoder.Decode(input, length, output, frame_size, decode_fec); } - /// - /// Decodes an opus encoded frame. - /// - /// Input payload. Use null to indicate packet loss - /// Number of bytes in payload. - /// Output signal (interleaved if 2 channels). length is frame_size*channels. - /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. - /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. - /// Number of decoded samples or . - /// - /// - public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + /// + public int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) { - ThrowIfDisposed(); - - fixed (byte* inputPtr = input) - fixed (short* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_decode(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0) - : NativeOpus.opus_decode(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0); - CheckError(result); - return result; - } + return _decoder.Decode(input, length, output, frame_size, decode_fec); } - /// - /// Decodes an opus encoded frame. - /// - /// Input payload. Use null to indicate packet loss - /// Number of bytes in payload. - /// Output signal (interleaved if 2 channels). length is frame_size*channels. - /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. - /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. - /// Number of decoded samples or . - /// - /// - public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + /// + public int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) { - ThrowIfDisposed(); - - fixed (byte* inputPtr = input) - fixed (int* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_decode24(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0) - : NativeOpus.opus_decode24(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0); - CheckError(result); - return result; - } + return _decoder.Decode(input, length, output, frame_size, decode_fec); } - /// - /// Decodes an opus encoded frame. - /// - /// Input payload. Use null to indicate packet loss - /// Number of bytes in payload. - /// Output signal (interleaved if 2 channels). length is (frame_size*channels)/2. Note: I don't know if this is correct. - /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. - /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. - /// Number of decoded samples or . - /// - /// - public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + /// + public int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) { - ThrowIfDisposed(); - - fixed (byte* inputPtr = input) - fixed (float* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_decode_float(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0) - : NativeOpus.opus_decode_float(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0); - CheckError(result); - return result; - } + return _decoder.Decode(input, length, output, frame_size, decode_fec); } #endif - /// - /// Decodes an opus encoded frame. - /// - /// Input payload. Use null to indicate packet loss - /// Number of bytes in payload. - /// Output signal (interleaved if 2 channels). length is frame_size*channels*sizeof(short). - /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. - /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. - /// Number of decoded samples or . - /// - /// - public unsafe int Decode(byte[]? input, int length, byte[] output, int frame_size, bool decode_fec) + /// + public int Decode(byte[]? input, int length, byte[] output, int frame_size, bool decode_fec) { - ThrowIfDisposed(); - - fixed (byte* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_decode(_handler, inputPtr, length, (short*)outputPtr, frame_size, - decode_fec ? 1 : 0) - : NativeOpus.opus_decode(_handler, inputPtr, length, (short*)outputPtr, frame_size, - decode_fec ? 1 : 0); - CheckError(result); - return result; - } + return _decoder.Decode(input, length, output, frame_size, decode_fec); } - /// - /// Decodes an opus encoded frame. - /// - /// Input payload. Use null to indicate packet loss - /// Number of bytes in payload. - /// Output signal (interleaved if 2 channels). length is frame_size*channels. - /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. - /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. - /// Number of decoded samples or . - /// - /// - public unsafe int Decode(byte[]? input, int length, short[] output, int frame_size, bool decode_fec) + /// + public int Decode(byte[]? input, int length, short[] output, int frame_size, bool decode_fec) { - ThrowIfDisposed(); - - fixed (byte* inputPtr = input) - fixed (short* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_decode(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0) - : NativeOpus.opus_decode(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0); - CheckError(result); - return result; - } + return _decoder.Decode(input, length, output, frame_size, decode_fec); } - /// - /// Decodes an opus encoded frame. - /// - /// Input payload. Use null to indicate packet loss - /// Number of bytes in payload. - /// Output signal (interleaved if 2 channels). length is frame_size*channels. - /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. - /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. - /// Number of decoded samples or . - /// - /// - public unsafe int Decode(byte[]? input, int length, int[] output, int frame_size, bool decode_fec) + /// + public int Decode(byte[]? input, int length, int[] output, int frame_size, bool decode_fec) { - ThrowIfDisposed(); - - fixed (byte* inputPtr = input) - fixed (int* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_decode24(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0) - : NativeOpus.opus_decode24(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0); - CheckError(result); - return result; - } + return _decoder.Decode(input, length, output, frame_size, decode_fec); } - /// - /// Decodes an opus encoded frame. - /// - /// Input payload. Use null to indicate packet loss - /// Number of bytes in payload. - /// Output signal (interleaved if 2 channels). length is (frame_size*channels)/2. Note: I don't know if this is correct. - /// Number of samples per channel of available space in pcm. If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=true), then frame_size needs to be exactly the duration of audio that is missing, otherwise the decoder will not be in the optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size must be a multiple of 2.5 ms. - /// Request that any in-band forward error correction data be decoded. If no such data is available, the frame is decoded as if it were lost. - /// Number of decoded samples or . - /// - /// - public unsafe int Decode(byte[]? input, int length, float[] output, int frame_size, bool decode_fec) + /// + public int Decode(byte[]? input, int length, float[] output, int frame_size, bool decode_fec) { - ThrowIfDisposed(); - - fixed (byte* inputPtr = input) - fixed (float* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_decode_float(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0) - : NativeOpus.opus_decode_float(_handler, inputPtr, length, outputPtr, frame_size, - decode_fec ? 1 : 0); - CheckError(result); - return result; - } + return _decoder.Decode(input, length, output, frame_size, decode_fec); } - /// - /// Performs a ctl request. - /// - /// The request you want to specify. - /// The result code of the request. See . - /// - /// + /// public int Ctl(DecoderCTL request) { - ThrowIfDisposed(); - var result = _useStatic - ? StaticNativeOpus.opus_decoder_ctl(_handler, (int)request) - : NativeOpus.opus_decoder_ctl(_handler, (int)request); - CheckError(result); - return result; + return _decoder.Ctl(request); } - /// - /// Performs a ctl set request. - /// - /// The request you want to specify. - /// The input value. - /// The result code of the request. See . - /// - /// + /// public int Ctl(DecoderCTL request, int value) { - ThrowIfDisposed(); - var result = _useStatic - ? StaticNativeOpus.opus_decoder_ctl(_handler, (int)request, value) - : NativeOpus.opus_decoder_ctl(_handler, (int)request, value); - CheckError(result); - return result; + return _decoder.Ctl(request, value); } - /// - /// Performs a ctl get/set request. - /// - /// The type you want to input/output. - /// The request you want to specify. - /// The input/output value. - /// The result code of the request. See . - /// - /// - public unsafe int Ctl(DecoderCTL request, ref T value) where T : unmanaged + /// + public int Ctl(DecoderCTL request, ref T value) where T : unmanaged { - ThrowIfDisposed(); - fixed (void* valuePtr = &value) - { - var result = _useStatic - ? StaticNativeOpus.opus_decoder_ctl(_handler, (int)request, valuePtr) - : NativeOpus.opus_decoder_ctl(_handler, (int)request, valuePtr); - CheckError(result); - return result; - } + return _decoder.Ctl(request, ref value); } - /// - /// Performs a ctl request. - /// - /// The request you want to specify. - /// The result code of the request. See . - /// - /// + /// public int Ctl(GenericCTL request) { - ThrowIfDisposed(); - var result = _useStatic - ? StaticNativeOpus.opus_decoder_ctl(_handler, (int)request) - : NativeOpus.opus_decoder_ctl(_handler, (int)request); - CheckError(result); - return result; + return _decoder.Ctl(request); } - /// - /// Performs a ctl set request. - /// - /// The request you want to specify. - /// The input value. - /// The result code of the request. See . - /// - /// + /// public int Ctl(GenericCTL request, int value) { - ThrowIfDisposed(); - var result = _useStatic - ? StaticNativeOpus.opus_decoder_ctl(_handler, (int)request, value) - : NativeOpus.opus_decoder_ctl(_handler, (int)request, value); - CheckError(result); - return result; - } - - /// - /// Performs a ctl request. - /// - /// The type you want to input/output. - /// The request you want to specify. - /// The input/output value. - /// The result code of the request. See . - /// - /// - public unsafe int Ctl(GenericCTL request, ref T value) where T : unmanaged - { - ThrowIfDisposed(); - fixed (void* valuePtr = &value) - { - var result = _useStatic - ? StaticNativeOpus.opus_decoder_ctl(_handler, (int)request, valuePtr) - : NativeOpus.opus_decoder_ctl(_handler, (int)request, valuePtr); - CheckError(result); - return result; - } + return _decoder.Ctl(request, value); } /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose logic. - /// - /// Set to true if fully disposing. - protected virtual void Dispose(bool disposing) - { - if (_disposed) return; - - if (disposing) - { - if (!_handler.IsClosed) - _handler.Close(); - } - - _disposed = true; - } - - /// - /// Throws an exception if this object is disposed or the handler is closed. - /// - /// - protected virtual void ThrowIfDisposed() - { - if (_disposed || _handler.IsClosed) - throw new ObjectDisposedException(GetType().FullName); - } - - /// - /// Checks if there is an opus error and throws if the error is a negative value. - /// - /// The error code to input. - /// - protected static void CheckError(int error) + public int Ctl(GenericCTL request, ref T value) where T : unmanaged { - if (error < 0) - throw new OpusException(((OpusErrorCodes)error).ToString()); + return _decoder.Ctl(request, ref value); } } } diff --git a/OpusSharp.Core/OpusEncoder.cs b/OpusSharp.Core/OpusEncoder.cs index a794c30..3ea6396 100644 --- a/OpusSharp.Core/OpusEncoder.cs +++ b/OpusSharp.Core/OpusEncoder.cs @@ -1,5 +1,5 @@ -using OpusSharp.Core.SafeHandlers; -using System; +using System; +using OpusSharp.Core.Interfaces; // ReSharper disable MemberCanBePrivate.Global // ReSharper disable FieldCanBeMadeReadOnly.Global @@ -9,15 +9,12 @@ namespace OpusSharp.Core /// /// An opus encoder. /// - public class OpusEncoder : IDisposable + public class OpusEncoder : IOpusEncoder { /// - /// Direct safe handle for the . IT IS NOT RECOMMENDED TO CLOSE THE HANDLE DIRECTLY! Instead, use to dispose the handle and object safely. + /// Direct opus encoder for the . You can close this directly. /// - protected OpusEncoderSafeHandle _handler; - - private readonly bool _useStatic; - private bool _disposed; + protected IOpusEncoder _encoder; /// /// Creates a new opus encoder. @@ -27,450 +24,128 @@ public class OpusEncoder : IDisposable /// Set to to force static imports, to force dynamic imports, or to auto-select based on platform. /// Coding mode (one of , or /// - public unsafe OpusEncoder(int sample_rate, int channels, OpusPredefinedValues application, + public OpusEncoder(int sample_rate, int channels, OpusPredefinedValues application, bool? use_static = null) { - _useStatic = OpusRuntime.ShouldUseStaticImports(use_static); - var error = 0; - _handler = _useStatic - ? StaticNativeOpus.opus_encoder_create(sample_rate, channels, (int)application, &error) - : NativeOpus.opus_encoder_create(sample_rate, channels, (int)application, &error); - CheckError(error); + var useStatic = OpusRuntime.ShouldUseStaticImports(use_static); + _encoder = useStatic + ? (IOpusEncoder)new Static.OpusEncoder(sample_rate, channels, application) + : new Dynamic.OpusEncoder(sample_rate, channels, application); } - /// - /// Opus encoder destructor. - /// - ~OpusEncoder() + /// + public void Dispose() { - Dispose(false); + _encoder.Dispose(); + GC.SuppressFinalize(this); } + #if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER - /// - /// Encodes a pcm frame. - /// - /// Input signal (interleaved if 2 channels). length is frame_size*channels. - /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. - /// Output payload. This must contain storage for at least max_data_bytes. - /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. - /// The length of the encoded packet (in bytes). - /// - /// - public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + /// + public int Encode(Span input, int frame_size, Span output, int max_data_bytes) { - ThrowIfDisposed(); - fixed (byte* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_encode(_handler, (short*)inputPtr, frame_size, outputPtr, max_data_bytes) - : NativeOpus.opus_encode(_handler, (short*)inputPtr, frame_size, outputPtr, max_data_bytes); - CheckError(result); - return result; - } + return _encoder.Encode(input, frame_size, output, max_data_bytes); } - /// - /// Encodes a pcm frame. - /// - /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(short). - /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. - /// Output payload. This must contain storage for at least max_data_bytes. - /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. - /// The length of the encoded packet (in bytes). - /// - /// - public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + /// + public int Encode(Span input, int frame_size, Span output, int max_data_bytes) { - ThrowIfDisposed(); - fixed (short* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_encode(_handler, inputPtr, frame_size, outputPtr, max_data_bytes) - : NativeOpus.opus_encode(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); - CheckError(result); - return result; - } + return _encoder.Encode(input, frame_size, output, max_data_bytes); } - - /// - /// Encodes a pcm frame. - /// - /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(int). - /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. - /// Output payload. This must contain storage for at least max_data_bytes. - /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. - /// The length of the encoded packet (in bytes). - /// - /// - public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + + /// + public int Encode(Span input, int frame_size, Span output, int max_data_bytes) { - ThrowIfDisposed(); - fixed (int* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_encode24(_handler, inputPtr, frame_size, outputPtr, max_data_bytes) - : NativeOpus.opus_encode24(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); - CheckError(result); - return result; - } + return _encoder.Encode(input, frame_size, output, max_data_bytes); } - /// - /// Encodes a floating point pcm frame. - /// - /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(float). - /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. - /// Output payload. This must contain storage for at least max_data_bytes. - /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. - /// The length of the encoded packet (in bytes). - /// - /// - public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + /// + public int Encode(Span input, int frame_size, Span output, int max_data_bytes) { - ThrowIfDisposed(); - fixed (float* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_encode_float(_handler, inputPtr, frame_size, outputPtr, max_data_bytes) - : NativeOpus.opus_encode_float(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); - CheckError(result); - return result; - } + return _encoder.Encode(input, frame_size, output, max_data_bytes); } #endif - /// - /// Encodes a pcm frame. - /// - /// Input signal (interleaved if 2 channels). length is frame_size*channels. - /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. - /// Output payload. This must contain storage for at least max_data_bytes. - /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. - /// The length of the encoded packet (in bytes). - /// - /// - public unsafe int Encode(byte[] input, int frame_size, byte[] output, int max_data_bytes) + /// + public int Encode(byte[] input, int frame_size, byte[] output, int max_data_bytes) { - ThrowIfDisposed(); - fixed (byte* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_encode(_handler, (short*)inputPtr, frame_size, outputPtr, max_data_bytes) - : NativeOpus.opus_encode(_handler, (short*)inputPtr, frame_size, outputPtr, max_data_bytes); - CheckError(result); - return result; - } + return _encoder.Encode(input, frame_size, output, max_data_bytes); } - /// - /// Encodes a pcm frame. - /// - /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(short). - /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. - /// Output payload. This must contain storage for at least max_data_bytes. - /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. - /// The length of the encoded packet (in bytes). - /// - /// - public unsafe int Encode(short[] input, int frame_size, byte[] output, int max_data_bytes) + /// + public int Encode(short[] input, int frame_size, byte[] output, int max_data_bytes) { - ThrowIfDisposed(); - fixed (short* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_encode(_handler, inputPtr, frame_size, outputPtr, max_data_bytes) - : NativeOpus.opus_encode(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); - CheckError(result); - return result; - } + return _encoder.Encode(input, frame_size, output, max_data_bytes); } - /// - /// Encodes a pcm frame. - /// - /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(int). - /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. - /// Output payload. This must contain storage for at least max_data_bytes. - /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. - /// The length of the encoded packet (in bytes). - /// - /// - public unsafe int Encode(int[] input, int frame_size, byte[] output, int max_data_bytes) + /// + public int Encode(int[] input, int frame_size, byte[] output, int max_data_bytes) { - ThrowIfDisposed(); - fixed (int* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_encode24(_handler, inputPtr, frame_size, outputPtr, max_data_bytes) - : NativeOpus.opus_encode24(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); - CheckError(result); - return result; - } + return _encoder.Encode(input, frame_size, output, max_data_bytes); } - /// - /// Encodes a floating point pcm frame. - /// - /// Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(float). - /// The frame size of the pcm data. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. - /// Output payload. This must contain storage for at least max_data_bytes. - /// Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use to control the bitrate. - /// The length of the encoded packet (in bytes). - /// - /// - public unsafe int Encode(float[] input, int frame_size, byte[] output, int max_data_bytes) + /// + public int Encode(float[] input, int frame_size, byte[] output, int max_data_bytes) { - ThrowIfDisposed(); - fixed (float* inputPtr = input) - fixed (byte* outputPtr = output) - { - var result = _useStatic - ? StaticNativeOpus.opus_encode_float(_handler, inputPtr, frame_size, outputPtr, max_data_bytes) - : NativeOpus.opus_encode_float(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); - CheckError(result); - return result; - } + return _encoder.Encode(input, frame_size, output, max_data_bytes); } - /// - /// Performs a ctl request. - /// - /// The request you want to specify. - /// The result code of the request. See . - /// - /// + /// public int Ctl(EncoderCTL request) { - ThrowIfDisposed(); - var result = _useStatic - ? StaticNativeOpus.opus_encoder_ctl(_handler, (int)request) - : NativeOpus.opus_encoder_ctl(_handler, (int)request); - CheckError(result); - return result; + return _encoder.Ctl(request); } - /// - /// Performs a ctl request. - /// - /// The request you want to specify. - /// /// The input value. - /// The result code of the request. See . - /// - /// + /// public int Ctl(EncoderCTL request, int value) { - ThrowIfDisposed(); - var result = _useStatic - ? StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, value) - : NativeOpus.opus_encoder_ctl(_handler, (int)request, value); - CheckError(result); - return result; + return _encoder.Ctl(request, value); } - /// - /// Performs a ctl request. - /// - /// The type you want to input/output. - /// The request you want to specify. - /// The input/output value. - /// The result code of the request. See . - /// - /// - public unsafe int Ctl(EncoderCTL request, ref T value) where T : unmanaged + /// + public int Ctl(EncoderCTL request, ref T value) where T : unmanaged { - ThrowIfDisposed(); - fixed (void* valuePtr = &value) - { - var result = _useStatic - ? StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr) - : NativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr); - CheckError(result); - return result; - } + return _encoder.Ctl(request, ref value); } - /// - /// Performs a ctl request. - /// - /// The type you want to input/output. - /// The request you want to specify. - /// The input/output value. - /// The second input value. - /// The result code of the request. See . - /// - /// - public unsafe int Ctl(EncoderCTL request, ref T value, int value2) + /// + public int Ctl(EncoderCTL request, ref T value, int value2) where T : unmanaged { - ThrowIfDisposed(); - fixed (void* valuePtr = &value) - { - var result = _useStatic - ? StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr, value2) - : NativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr, value2); - CheckError(result); - return result; - } + return _encoder.Ctl(request, ref value, value2); } - /// - /// Performs a ctl request. - /// - /// The type you want to input/output. - /// The request you want to specify. - /// The input value. - /// The second input/output value. - /// The result code of the request. See . - /// - /// - public unsafe int Ctl(EncoderCTL request, int value, ref T value2) + /// + public int Ctl(EncoderCTL request, int value, ref T value2) where T : unmanaged { - ThrowIfDisposed(); - fixed (void* value2Ptr = &value2) - { - var result = _useStatic - ? StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, value, value2Ptr) - : NativeOpus.opus_encoder_ctl(_handler, (int)request, value, value2Ptr); - CheckError(result); - return result; - } + return _encoder.Ctl(request, value, ref value2); } - /// - /// Performs a ctl request. - /// - /// The type you want to input/output. - /// The second type you want to input/output. - /// The request you want to specify. - /// The input/output value. - /// The second input/output value. - /// The result code of the request. See . - /// - /// - public unsafe int Ctl(EncoderCTL request, ref T value, ref T2 value2) + /// + public int Ctl(EncoderCTL request, ref T value, ref T2 value2) where T : unmanaged where T2 : unmanaged { - ThrowIfDisposed(); - fixed (void* valuePtr = &value) - fixed (void* value2Ptr = &value2) - { - var result = _useStatic - ? StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr, value2Ptr) - : NativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr, value2Ptr); - CheckError(result); - return result; - } + return _encoder.Ctl(request, ref value, ref value2); } - /// - /// Performs a ctl request. - /// - /// The request you want to specify. - /// The result code of the request. See . - /// - /// + /// public int Ctl(GenericCTL request) { - ThrowIfDisposed(); - var result = _useStatic - ? StaticNativeOpus.opus_encoder_ctl(_handler, (int)request) - : NativeOpus.opus_encoder_ctl(_handler, (int)request); - CheckError(result); - return result; + return _encoder.Ctl(request); } - /// - /// Performs a ctl set request. - /// - /// The request you want to specify. - /// The input value. - /// The result code of the request. See . - /// - /// + /// public int Ctl(GenericCTL request, int value) { - ThrowIfDisposed(); - var result = _useStatic - ? StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, value) - : NativeOpus.opus_encoder_ctl(_handler, (int)request, value); - CheckError(result); - return result; - } - - /// - /// Performs a ctl request. - /// - /// The type you want to input/output. - /// The request you want to specify. - /// The input/output value. - /// The result code of the request. See . - /// - /// - public unsafe int Ctl(GenericCTL request, ref T value) where T : unmanaged - { - ThrowIfDisposed(); - fixed (void* valuePtr = &value) - { - var result = _useStatic - ? StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr) - : NativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr); - CheckError(result); - return result; - } + return _encoder.Ctl(request, value); } /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose logic. - /// - /// Set to true if fully disposing. - protected virtual void Dispose(bool disposing) - { - if (_disposed) return; - - if (disposing) - { - if (!_handler.IsClosed) - _handler.Close(); - } - - _disposed = true; - } - - /// - /// Throws an exception if this object is disposed or the handler is closed. - /// - /// - protected virtual void ThrowIfDisposed() - { - if (_disposed || _handler.IsClosed) - throw new ObjectDisposedException(GetType().FullName); - } - - /// - /// Checks if there is an opus error and throws if the error is a negative value. - /// - /// The error code to input. - /// - protected static void CheckError(int error) + public int Ctl(GenericCTL request, ref T value) where T : unmanaged { - if (error < 0) - throw new OpusException(((OpusErrorCodes)error).ToString()); + return _encoder.Ctl(request, ref value); } } -} +} \ No newline at end of file diff --git a/OpusSharp.Core/OpusInfo.cs b/OpusSharp.Core/OpusInfo.cs index 4c7c0a8..8f7deca 100644 --- a/OpusSharp.Core/OpusInfo.cs +++ b/OpusSharp.Core/OpusInfo.cs @@ -1,7 +1,4 @@ -using System; -using System.Runtime.InteropServices; - -// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable MemberCanBePrivate.Global // ReSharper disable FieldCanBeMadeReadOnly.Global // ReSharper disable InconsistentNaming namespace OpusSharp.Core @@ -16,13 +13,10 @@ public class OpusInfo /// /// Set to to force static imports, to force dynamic imports, or to auto-select based on platform. /// Version string. - public static unsafe string Version(bool? use_static = null) + public static string Version(bool? use_static = null) { var useStaticImports = OpusRuntime.ShouldUseStaticImports(use_static); - var version = useStaticImports - ? StaticNativeOpus.opus_get_version_string() - : NativeOpus.opus_get_version_string(); - return Marshal.PtrToStringAnsi((IntPtr)version) ?? ""; + return useStaticImports ? Static.OpusInfo.Version() : Dynamic.OpusInfo.Version(); } /// @@ -31,11 +25,10 @@ public static unsafe string Version(bool? use_static = null) /// Error number. /// Set to to force static imports, to force dynamic imports, or to auto-select based on platform. /// Error string. - public static unsafe string StringError(int error, bool? use_static = null) + public static string StringError(int error, bool? use_static = null) { var useStaticImports = OpusRuntime.ShouldUseStaticImports(use_static); - var stringError = useStaticImports ? StaticNativeOpus.opus_strerror(error) : NativeOpus.opus_strerror(error); - return Marshal.PtrToStringAnsi((IntPtr)stringError) ?? ""; + return useStaticImports ? Static.OpusInfo.StringError(error) : Dynamic.OpusInfo.StringError(error); } } } diff --git a/OpusSharp.Core/OpusRuntime.cs b/OpusSharp.Core/OpusRuntime.cs index 94dde3e..273b7ee 100644 --- a/OpusSharp.Core/OpusRuntime.cs +++ b/OpusSharp.Core/OpusRuntime.cs @@ -6,12 +6,7 @@ internal static class OpusRuntime { public static bool ShouldUseStaticImports(bool? useStatic) { - if (useStatic.HasValue) - { - return useStatic.Value; - } - - return IsStaticallyLinkedPlatform(); + return useStatic ?? IsStaticallyLinkedPlatform(); } private static bool IsStaticallyLinkedPlatform() diff --git a/OpusSharp.Core/Static/OpusDecoder.cs b/OpusSharp.Core/Static/OpusDecoder.cs new file mode 100644 index 0000000..483ee7f --- /dev/null +++ b/OpusSharp.Core/Static/OpusDecoder.cs @@ -0,0 +1,271 @@ +using System; +using OpusSharp.Core.Interfaces; +using OpusSharp.Core.SafeHandlers; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +// ReSharper disable ClassNeverInstantiated.Global +namespace OpusSharp.Core.Static +{ + /// + /// An opus decoder using static binding calls. + /// + public class OpusDecoder : IOpusDecoder + { + /// + /// Direct safe handle for the . IT IS NOT RECOMMENDED TO CLOSE THE HANDLE DIRECTLY! Instead, use to dispose the handle and object safely. + /// + protected OpusDecoderSafeHandle _handler; + + private bool _disposed; + + /// + /// Creates a new opus decoder. + /// + /// The sample rate, this must be one of 8000, 12000, 16000, 24000, or 48000. + /// Number of channels, this must be 1 or 2. + /// + public unsafe OpusDecoder(int sample_rate, int channels) + { + var error = 0; + _handler = StaticNativeOpus.opus_decoder_create(sample_rate, channels, &error); + CheckError(error); + } + + /// + /// Opus decoder destructor. + /// + ~OpusDecoder() + { + Dispose(false); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + +#if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER + /// + public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_decode(_handler, inputPtr, length, (short*)outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (short* outputPtr = output) + { + var result = StaticNativeOpus.opus_decode(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (int* outputPtr = output) + { + var result = StaticNativeOpus.opus_decode24(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(Span input, int length, Span output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (float* outputPtr = output) + { + var result = StaticNativeOpus.opus_decode_float(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } +#endif + + /// + public unsafe int Decode(byte[]? input, int length, byte[] output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_decode(_handler, inputPtr, length, (short*)outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(byte[]? input, int length, short[] output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (short* outputPtr = output) + { + var result = StaticNativeOpus.opus_decode(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(byte[]? input, int length, int[] output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (int* outputPtr = output) + { + var result = StaticNativeOpus.opus_decode24(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public unsafe int Decode(byte[]? input, int length, float[] output, int frame_size, bool decode_fec) + { + ThrowIfDisposed(); + + fixed (byte* inputPtr = input) + fixed (float* outputPtr = output) + { + var result = StaticNativeOpus.opus_decode_float(_handler, inputPtr, length, outputPtr, frame_size, + decode_fec ? 1 : 0); + CheckError(result); + return result; + } + } + + /// + public int Ctl(DecoderCTL request) + { + ThrowIfDisposed(); + var result = StaticNativeOpus.opus_decoder_ctl(_handler, (int)request); + CheckError(result); + return result; + } + + /// + public int Ctl(DecoderCTL request, int value) + { + ThrowIfDisposed(); + var result = StaticNativeOpus.opus_decoder_ctl(_handler, (int)request, value); + CheckError(result); + return result; + } + + /// + public unsafe int Ctl(DecoderCTL request, ref T value) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = StaticNativeOpus.opus_decoder_ctl(_handler, (int)request, valuePtr); + CheckError(result); + return result; + } + } + + /// + public int Ctl(GenericCTL request) + { + ThrowIfDisposed(); + var result = StaticNativeOpus.opus_decoder_ctl(_handler, (int)request); + CheckError(result); + return result; + } + + /// + public int Ctl(GenericCTL request, int value) + { + ThrowIfDisposed(); + var result = StaticNativeOpus.opus_decoder_ctl(_handler, (int)request, value); + CheckError(result); + return result; + } + + /// + public unsafe int Ctl(GenericCTL request, ref T value) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = StaticNativeOpus.opus_decoder_ctl(_handler, (int)request, valuePtr); + CheckError(result); + return result; + } + } + + /// + /// Dispose logic. + /// + /// Set to true if fully disposing. + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + + if (disposing) + { + if (!_handler.IsClosed) + _handler.Close(); + } + + _disposed = true; + } + + /// + /// Throws an exception if this object is disposed or the handler is closed. + /// + /// + protected virtual void ThrowIfDisposed() + { + if (_disposed || _handler.IsClosed) + throw new ObjectDisposedException(GetType().FullName); + } + + /// + /// Checks if there is an opus error and throws if the error is a negative value. + /// + /// The error code to input. + /// + protected static void CheckError(int error) + { + if (error < 0) + throw new OpusException(((OpusErrorCodes)error).ToString()); + } + } +} \ No newline at end of file diff --git a/OpusSharp.Core/Static/OpusEncoder.cs b/OpusSharp.Core/Static/OpusEncoder.cs new file mode 100644 index 0000000..0789782 --- /dev/null +++ b/OpusSharp.Core/Static/OpusEncoder.cs @@ -0,0 +1,299 @@ +using OpusSharp.Core.SafeHandlers; +using System; +using OpusSharp.Core.Interfaces; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +namespace OpusSharp.Core.Static +{ + /// + /// An opus encoder using static binding calls. + /// + public class OpusEncoder : IOpusEncoder + { + /// + /// Direct safe handle for the . IT IS NOT RECOMMENDED TO CLOSE THE HANDLE DIRECTLY! Instead, use to dispose the handle and object safely. + /// + protected OpusEncoderSafeHandle _handler; + + private bool _disposed; + + /// + /// Creates a new opus encoder. + /// + /// The sample rate, this must be one of 8000, 12000, 16000, 24000, or 48000. + /// Number of channels, this must be 1 or 2. + /// Coding mode (one of , or + /// + public unsafe OpusEncoder(int sample_rate, int channels, OpusPredefinedValues application) + { + var error = 0; + _handler = StaticNativeOpus.opus_encoder_create(sample_rate, channels, (int)application, &error); + CheckError(error); + } + + /// + /// Opus encoder destructor. + /// + ~OpusEncoder() + { + Dispose(false); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + +#if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER + /// + public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (byte* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_encode(_handler, (short*)inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (short* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_encode(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (int* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_encode24(_handler, inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(Span input, int frame_size, Span output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (float* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_encode_float(_handler, inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } +#endif + + /// + public unsafe int Encode(byte[] input, int frame_size, byte[] output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (byte* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_encode(_handler, (short*)inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(short[] input, int frame_size, byte[] output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (short* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_encode(_handler, inputPtr, frame_size, outputPtr, max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(int[] input, int frame_size, byte[] output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (int* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_encode24(_handler, inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public unsafe int Encode(float[] input, int frame_size, byte[] output, int max_data_bytes) + { + ThrowIfDisposed(); + fixed (float* inputPtr = input) + fixed (byte* outputPtr = output) + { + var result = StaticNativeOpus.opus_encode_float(_handler, inputPtr, frame_size, outputPtr, + max_data_bytes); + CheckError(result); + return result; + } + } + + /// + public int Ctl(EncoderCTL request) + { + ThrowIfDisposed(); + var result = StaticNativeOpus.opus_encoder_ctl(_handler, (int)request); + CheckError(result); + return result; + } + + /// + public int Ctl(EncoderCTL request, int value) + { + ThrowIfDisposed(); + var result = StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, value); + CheckError(result); + return result; + } + + /// + public unsafe int Ctl(EncoderCTL request, ref T value) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr); + CheckError(result); + return result; + } + } + + /// + public unsafe int Ctl(EncoderCTL request, ref T value, int value2) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr, value2); + CheckError(result); + return result; + } + } + + /// + public unsafe int Ctl(EncoderCTL request, int value, ref T value2) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* value2Ptr = &value2) + { + var result = StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, value, value2Ptr); + CheckError(result); + return result; + } + } + + /// + public unsafe int Ctl(EncoderCTL request, ref T value, ref T2 value2) + where T : unmanaged where T2 : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + fixed (void* value2Ptr = &value2) + { + var result = StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr, value2Ptr); + CheckError(result); + return result; + } + } + + /// + public int Ctl(GenericCTL request) + { + ThrowIfDisposed(); + var result = StaticNativeOpus.opus_encoder_ctl(_handler, (int)request); + CheckError(result); + return result; + } + + /// + public int Ctl(GenericCTL request, int value) + { + ThrowIfDisposed(); + var result = StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, value); + CheckError(result); + return result; + } + + /// + public unsafe int Ctl(GenericCTL request, ref T value) where T : unmanaged + { + ThrowIfDisposed(); + fixed (void* valuePtr = &value) + { + var result = StaticNativeOpus.opus_encoder_ctl(_handler, (int)request, valuePtr); + CheckError(result); + return result; + } + } + + /// + /// Dispose logic. + /// + /// Set to true if fully disposing. + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + + if (disposing) + { + if (!_handler.IsClosed) + _handler.Close(); + } + + _disposed = true; + } + + /// + /// Throws an exception if this object is disposed or the handler is closed. + /// + /// + protected virtual void ThrowIfDisposed() + { + if (_disposed || _handler.IsClosed) + throw new ObjectDisposedException(GetType().FullName); + } + + /// + /// Checks if there is an opus error and throws if the error is a negative value. + /// + /// The error code to input. + /// + protected static void CheckError(int error) + { + if (error < 0) + throw new OpusException(((OpusErrorCodes)error).ToString()); + } + } +} \ No newline at end of file diff --git a/OpusSharp.Core/Static/OpusInfo.cs b/OpusSharp.Core/Static/OpusInfo.cs new file mode 100644 index 0000000..4af574d --- /dev/null +++ b/OpusSharp.Core/Static/OpusInfo.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.InteropServices; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +namespace OpusSharp.Core.Static +{ + /// + /// Provides information about the opus DLL. + /// + public class OpusInfo + { + /// + /// Gets the libopus version string. + /// + /// Version string. + public static unsafe string Version() + { + var version = StaticNativeOpus.opus_get_version_string(); + return Marshal.PtrToStringAnsi((IntPtr)version) ?? ""; + } + + /// + /// Converts an opus error code into a human-readable string. + /// + /// Error number. + /// Error string. + public static unsafe string StringError(int error) + { + var stringError = StaticNativeOpus.opus_strerror(error); + return Marshal.PtrToStringAnsi((IntPtr)stringError) ?? ""; + } + } +} diff --git a/OpusSharp.Core/StaticNativeOpus.cs b/OpusSharp.Core/StaticNativeOpus.cs index 441e325..06446b5 100644 --- a/OpusSharp.Core/StaticNativeOpus.cs +++ b/OpusSharp.Core/StaticNativeOpus.cs @@ -12,11 +12,7 @@ namespace OpusSharp.Core /// public static partial class StaticNativeOpus { -#if COMPILE_STATIC - private const string DllName = "__Internal"; -#else - private const string DllName = "opus"; -#endif + private const string DllName = "__Internal"; //Encoder ///