using Concentus;
using System;
/*
* Opus encoding and decoding are VERY important for any real world use of UniVoice as without
* encoding the size of audio data is much (over 10x) larger.
* For more info see https://www.github.com/adrenak/concentus-unity
*/
namespace Adrenak.UniVoice.Filters {
///
/// Decodes Opus encoded audio. Use this as a filter for incoming client audio.
///
public class ConcentusDecodeFilter : IAudioFilter {
IOpusDecoder decoder;
float[] decodeBuffer;
int inputChannelCount;
int inputFrequency;
byte[] floatsToBytes;
///
/// Creates a Concentus decode filter.
///
///
/// The length of the decode buffer. Default is 11520 to fit a large sample
/// with frequency 48000, duration 120ms and 2 channels. This should be enough
/// for almost all scenarios. Increase if you need more.
///
public ConcentusDecodeFilter(int decodeBufferLength = 11520) {
decodeBuffer = new float[decodeBufferLength];
}
public AudioFrame Run(AudioFrame input) {
inputChannelCount = input.channelCount;
inputFrequency = input.frequency;
CreateNewDecoderIfNeeded();
var decodeResult = Decode(input.samples, out Span decoded);
if (decodeResult > 0) {
floatsToBytes = Utils.Bytes.FloatsToBytes(decoded.ToArray());
return new AudioFrame {
timestamp = input.timestamp,
samples = floatsToBytes,
channelCount = inputChannelCount,
frequency = inputFrequency
};
}
else {
return new AudioFrame {
timestamp = input.timestamp,
samples = new byte[0],
channelCount = inputChannelCount,
frequency = inputFrequency
};
}
}
int Decode(Span toDecode, out Span decoded) {
// Decode the Opus packet into preallocated buffer
int samplesPerChannel = decoder.Decode(toDecode, decodeBuffer, decodeBuffer.Length);
if (samplesPerChannel > 0) {
int totalSamples = samplesPerChannel * inputChannelCount; // Total samples across all channels
decoded = decodeBuffer.AsSpan(0, totalSamples); // Trim to valid samples
}
else {
decoded = Span.Empty;
}
return samplesPerChannel; // Return number of samples per channel or 0 on failure
}
void CreateNewDecoderIfNeeded() {
if (decoder == null || decoder.SampleRate != inputFrequency || decoder.NumChannels != inputChannelCount) {
decoder?.Dispose();
decoder = OpusCodecFactory.CreateDecoder(inputFrequency, inputChannelCount);
}
}
}
}