Recursive AI

Decode Opus in C#

All oneaudio recipes

Decode Ogg Opus files in C# with oneaudio, convert them to your app’s sample rate, and decode raw Opus packets from a voice or network stream, with packet-loss concealment.

C#
using OneAudio;
using OneAudio.Opus;
 
// Ogg Opus files always decode at 48 kHz, whatever rate the encoder was given.
using (IAudioDecoder decoder = Audio.Open("music.opus"))
{
    AudioFormat f = decoder.Format;
    Console.WriteLine($"music.opus: {decoder.GetType().Name}, {f.SampleRate} Hz, " +
                      $"{f.Channels} ch, {decoder.LengthFrames / 48000.0:F3} s");
}
 
// Load it at the rate your app uses.
AudioClip clip = Audio.Load("music.opus", new LoadOptions { SampleRate = 44100 });
Console.WriteLine($"as 44.1 kHz: {clip.Frames} frames");
 
// Raw Opus packets, from a voice chat or a WebRTC stream, go through
// OpusDecoder. Here they come from OpusEncoder: 20 ms of a 440 Hz tone each.
var encoder = new OpusEncoder(new OpusEncoderSettings
{
    Channels = 1,
    Application = OpusApplication.Voip,
    BitrateBps = 24_000,
});
var packetDecoder = new OpusDecoder(sampleRate: 48000, channels: 1);
float[] pcm = new float[960];                               // 20 ms at 48 kHz
float[] decoded = new float[OpusPacket.MaxSamplesPer48k];   // the longest packet
byte[] packet = new byte[1500];
int bytes = 0, samples = 0;
for (int p = 0; p < 50; p++)                                // one second
{
    for (int i = 0; i < pcm.Length; i++)
        pcm[i] = 0.3f * MathF.Sin(2 * MathF.PI * 440 * (p * 960 + i) / 48000f);
    int length = encoder.Encode(pcm, 960, packet);
    bytes += length;
    samples += packetDecoder.Decode(packet.AsSpan(0, length), decoded);
}
Console.WriteLine($"50 packets, {bytes:N0} bytes, {samples} samples decoded");
 
// A lost packet: the decoder fills the gap. The span's length sets how much,
// here one 20 ms frame.
int concealed = packetDecoder.DecodePacketLoss(decoded.AsSpan(0, 960));
Console.WriteLine($"concealed {concealed} samples");
Text
music.opus: OpusStreamDecoder, 48000 Hz, 2 ch, 32.000 s
as 44.1 kHz: 1411200 frames
50 packets, 3,161 bytes, 48000 samples decoded
concealed 960 samples

Files and packets

  • Ogg Opus files (.opus, and .ogg files holding Opus) open with Audio.Open or Audio.Load like any other format. They always decode at 48 kHz, the rate Opus works at internally, even when the encoder was given 44.1 kHz audio. The pre-skip and the output gain in the header are applied, and the last granule position trims the end, so the length is the length of the original audio.
  • Raw packets go through OpusDecoder. Construct it with the output rate, 48 or 24 kHz, and one or two channels. A stereo stream is downmixed to mono, or a mono stream copied to both channels, to match. Decode returns the samples per channel it wrote. Give it a buffer of OpusPacket.MaxSamplesPer48k samples per channel, enough for the longest packet (120 ms).
  • A lost packet: call DecodePacketLoss with a span as long as the gap, here 20 ms, instead of Decode.

Good to know

  • The decoder passes all 12 RFC 8251 test vectors, on both the normal and the no-phase-inversion sets.
  • It decodes SILK, CELT and hybrid packets. Ogg Opus files can have up to eight channels (channel mapping family 1), and chained files play as one stream.
  • Its CELT packet-loss concealment follows its own rules rather than the reference decoder’s, and the conformance vectors don’t test it.
  • To write Opus, see Encode Opus.

Opus files and raw packets, compared with Concentus and OpusSharp: Opus in C#.