---
title: "Decode Opus in C#"
url: https://recursiveai.org/oneaudio/cookbook/decode-opus/
description: "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…"
version: "1.2.1"
---

# Decode Opus in C#

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.

```csharp
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](https://recursiveai.org/oneaudio/cookbook/encode-opus/).

Opus files and raw packets, compared with Concentus and OpusSharp: [Opus in C#](https://recursiveai.org/oneaudio/articles/opus-csharp/).
