Initial outgoing audio support

This commit is contained in:
Brandon Smith
2015-08-24 17:21:51 -03:00
parent 74f1272737
commit 74517d0ef3
10 changed files with 243 additions and 372 deletions

View File

@@ -2,180 +2,90 @@
namespace Opus.Net
{
/// <summary>
/// Opus codec wrapper.
/// </summary>
/// <summary> Opus codec wrapper. </summary>
public class OpusEncoder : IDisposable
{
/// <summary>
/// Creates a new Opus encoder.
/// </summary>
/// <param name="inputSamplingRate">Sampling rate of the input signal (Hz). This must be one of 8000, 12000, 16000, 24000, or 48000.</param>
/// <param name="inputChannels">Number of channels (1 or 2) in input signal.</param>
private readonly IntPtr _encoder;
/// <summary> Gets the bit rate of the encoder. </summary>
public const int BitRate = 16;
/// <summary> Gets the input sampling rate of the encoder. </summary>
public int InputSamplingRate { get; private set; }
/// <summary> Gets the number of channels of the encoder. </summary>
public int InputChannels { get; private set; }
/// <summary> Gets the milliseconds per frame. </summary>
public int FrameLength { get; private set; }
/// <summary> Gets the number of samples per frame. </summary>
public int SamplesPerFrame { get; private set; }
/// <summary> Gets the bytes per sample. </summary>
public int SampleSize { get; private set; }
/// <summary> Gets the bytes per frame. </summary>
public int FrameSize { get; private set; }
/// <summary> Gets the coding mode of the encoder. </summary>
public Application Application { get; private set; }
/// <summary> Creates a new Opus encoder. </summary>
/// <param name="samplingRate">Sampling rate of the input signal (Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000.</param>
/// <param name="channels">Number of channels (1 or 2) in input signal.</param>
/// <param name="frameLength">Length, in milliseconds, that each frame takes. Supported Values: 2.5, 5, 10, 20, 40, 60</param>
/// <param name="application">Coding mode.</param>
/// <returns>A new <c>OpusEncoder</c></returns>
public static OpusEncoder Create(int inputSamplingRate, int inputChannels, Application application)
public OpusEncoder(int samplingRate, int channels, int frameLength, Application application)
{
if (inputSamplingRate != 8000 &&
inputSamplingRate != 12000 &&
inputSamplingRate != 16000 &&
inputSamplingRate != 24000 &&
inputSamplingRate != 48000)
if (samplingRate != 8000 && samplingRate != 12000 &&
samplingRate != 16000 && samplingRate != 24000 &&
samplingRate != 48000)
throw new ArgumentOutOfRangeException("inputSamplingRate");
if (inputChannels != 1 && inputChannels != 2)
if (channels != 1 && channels != 2)
throw new ArgumentOutOfRangeException("inputChannels");
IntPtr error;
IntPtr encoder = API.opus_encoder_create(inputSamplingRate, inputChannels, (int)application, out error);
if ((Errors)error != Errors.OK)
{
throw new Exception("Exception occured while creating encoder");
}
return new OpusEncoder(encoder, inputSamplingRate, inputChannels, application);
}
private IntPtr _encoder;
private OpusEncoder(IntPtr encoder, int inputSamplingRate, int inputChannels, Application application)
{
_encoder = encoder;
InputSamplingRate = inputSamplingRate;
InputChannels = inputChannels;
InputSamplingRate = samplingRate;
InputChannels = channels;
Application = application;
MaxDataBytes = 4000;
FrameLength = frameLength;
SampleSize = (BitRate / 8) * channels;
SamplesPerFrame = samplingRate / 1000 * FrameLength;
FrameSize = SamplesPerFrame * SampleSize;
Error error;
_encoder = API.opus_encoder_create(samplingRate, channels, (int)application, out error);
if (error != Error.OK)
throw new InvalidOperationException("Error occured while creating encoder: " + error.ToString());
SetForwardErrorCorrection(true);
}
/// <summary>
/// Produces Opus encoded audio from PCM samples.
/// </summary>
/// <param name="inputPcmSamples">PCM samples to encode.</param>
/// <param name="sampleLength">How many bytes to encode.</param>
/// <param name="encodedLength">Set to length of encoded audio.</param>
/// <summary> Produces Opus encoded audio from PCM samples. </summary>
/// <param name="pcmSamples">PCM samples to encode.</param>
/// <param name="encodedLength">Length of encoded audio.</param>
/// <returns>Opus encoded audio buffer.</returns>
public unsafe byte[] Encode(byte[] inputPcmSamples, int sampleLength, out int encodedLength)
public unsafe int EncodeFrame(byte[] pcmSamples, byte[] outputBuffer)
{
if (disposed)
throw new ObjectDisposedException("OpusEncoder");
int frames = FrameCount(inputPcmSamples);
IntPtr encodedPtr;
byte[] encoded = new byte[MaxDataBytes];
int length = 0;
fixed (byte* benc = encoded)
fixed (byte* bPtr = outputBuffer)
{
encodedPtr = new IntPtr((void*)benc);
length = API.opus_encode(_encoder, inputPcmSamples, frames, encodedPtr, sampleLength);
encodedPtr = new IntPtr((void*)bPtr);
length = API.opus_encode(_encoder, pcmSamples, SamplesPerFrame, encodedPtr, outputBuffer.Length);
}
encodedLength = length;
if (length < 0)
throw new Exception("Encoding failed - " + ((Errors)length).ToString());
return encoded;
throw new Exception("Encoding failed: " + ((Error)length).ToString());
return length;
}
/// <summary>
/// Determines the number of frames in the PCM samples.
/// </summary>
/// <param name="pcmSamples"></param>
/// <returns></returns>
public int FrameCount(byte[] pcmSamples)
/// <summary> Gets or sets whether Forward Error Correction is enabled. </summary>
public void SetForwardErrorCorrection(bool value)
{
// seems like bitrate should be required
int bitrate = 16;
int bytesPerSample = (bitrate / 8) * InputChannels;
return pcmSamples.Length / bytesPerSample;
}
if (_encoder == IntPtr.Zero)
throw new ObjectDisposedException("OpusEncoder");
/// <summary>
/// Helper method to determine how many bytes are required for encoding to work.
/// </summary>
/// <param name="frameCount">Target frame size.</param>
/// <returns></returns>
public int FrameByteCount(int frameCount)
{
int bitrate = 16;
int bytesPerSample = (bitrate / 8) * InputChannels;
return frameCount * bytesPerSample;
}
/// <summary>
/// Gets the input sampling rate of the encoder.
/// </summary>
public int InputSamplingRate { get; private set; }
/// <summary>
/// Gets the number of channels of the encoder.
/// </summary>
public int InputChannels { get; private set; }
/// <summary>
/// Gets the coding mode of the encoder.
/// </summary>
public Application Application { get; private set; }
/// <summary>
/// Gets or sets the size of memory allocated for reading encoded data.
/// 4000 is recommended.
/// </summary>
public int MaxDataBytes { get; set; }
/// <summary>
/// Gets or sets the bitrate setting of the encoding.
/// </summary>
public int Bitrate
{
get
{
if (disposed)
throw new ObjectDisposedException("OpusEncoder");
int bitrate;
var ret = API.opus_encoder_ctl(_encoder, Ctl.GetBitrateRequest, out bitrate);
if (ret < 0)
throw new Exception("Encoder error - " + ((Errors)ret).ToString());
return bitrate;
}
set
{
if (disposed)
throw new ObjectDisposedException("OpusEncoder");
var ret = API.opus_encoder_ctl(_encoder, Ctl.SetBitrateRequest, value);
if (ret < 0)
throw new Exception("Encoder error - " + ((Errors)ret).ToString());
}
}
/// <summary>
/// Gets or sets whether Forward Error Correction is enabled.
/// </summary>
public bool ForwardErrorCorrection
{
get
{
if (_encoder == IntPtr.Zero)
throw new ObjectDisposedException("OpusEncoder");
int fec;
int ret = API.opus_encoder_ctl(_encoder, Ctl.GetInbandFECRequest, out fec);
if (ret < 0)
throw new Exception("Encoder error - " + ((Errors)ret).ToString());
return fec > 0;
}
set
{
if (_encoder == IntPtr.Zero)
throw new ObjectDisposedException("OpusEncoder");
var ret = API.opus_encoder_ctl(_encoder, Ctl.SetInbandFECRequest, value ? 1 : 0);
if (ret < 0)
throw new Exception("Encoder error - " + ((Errors)ret).ToString());
}
}
~OpusEncoder()
{
Dispose();
var ret = API.opus_encoder_ctl(_encoder, Ctl.SetInbandFECRequest, value ? 1 : 0);
if (ret < 0)
throw new Exception("Encoder error - " + ((Error)ret).ToString());
}
private bool disposed;
@@ -187,12 +97,13 @@ namespace Opus.Net
GC.SuppressFinalize(this);
if (_encoder != IntPtr.Zero)
{
API.opus_encoder_destroy(_encoder);
_encoder = IntPtr.Zero;
}
disposed = true;
}
~OpusEncoder()
{
Dispose();
}
}
}