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.
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");
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.oggfiles holding Opus) open withAudio.OpenorAudio.Loadlike 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.Decodereturns the samples per channel it wrote. Give it a buffer ofOpusPacket.MaxSamplesPer48ksamples per channel, enough for the longest packet (120 ms). - A lost packet: call
DecodePacketLosswith a span as long as the gap, here 20 ms, instead ofDecode.
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#.