Recursive AI

Encode Opus in C#

All oneaudio recipes

Encode WAV, FLAC or MP3 to Ogg Opus in C# with oneaudio, a managed Opus encoder: choose the bitrate and mode, add tags, and encode speech with the VoIP application.

C#
using OneAudio;
using OneAudio.Opus;
 
// One call with the defaults: VBR at the encoder's automatic bitrate.
Audio.Transcode("music.wav", "music-auto.opus");
Console.WriteLine($"music-auto.opus: {new FileInfo("music-auto.opus").Length:N0} bytes");
 
// Choose the bitrate and add tags.
var settings = new OpusFileWriterSettings
{
    Encoder = new OpusEncoderSettings
    {
        BitrateBps = 64_000,
        BitrateMode = OpusBitrateMode.ConstrainedVbr,
    },
    Comments = ["TITLE=Four chords", "ARTIST=Test signal"],
};
Audio.Transcode("music.flac", "music-64k.opus", new TranscodeOptions { Opus = settings });
Console.WriteLine($"music-64k.opus: {new FileInfo("music-64k.opus").Length:N0} bytes");
 
// Speech: the VoIP application, mono, 16 kbit/s. The writer resamples the
// 16 kHz input to 48 kHz.
AudioClip voice = Audio.Load("voice.wav");
var voiceSettings = new OpusFileWriterSettings
{
    Channels = 1,
    InputSampleRate = voice.Format.SampleRate,
    Encoder = new OpusEncoderSettings
    {
        Application = OpusApplication.Voip,
        BitrateBps = 16_000,
    },
};
using (var writer = new OpusFileWriter(File.Create("voice-16k.opus"), voiceSettings,
                                       leaveOpen: false))
{
    writer.Write(voice.Samples.Span);       // call as often as you like
    writer.Finish();
    Console.WriteLine($"voice-16k.opus: {writer.FramesWritten} frames, pre-skip {writer.PreSkip}, " +
                      $"{new FileInfo("voice-16k.opus").Length:N0} bytes");
}
 
// Read them back: the decoded length is the audio written, at 48 kHz.
foreach (string f in new[] { "music-64k.opus", "voice-16k.opus" })
{
    using IAudioDecoder d = Audio.Open(f);
    Console.WriteLine($"{f}: {d.Format.SampleRate} Hz, {d.Format.Channels} ch, " +
                      $"{d.LengthFrames / 48000.0:F3} s");
}
Text
music-auto.opus: 404,461 bytes
music-64k.opus: 260,511 bytes
voice-16k.opus: 289201 frames, pre-skip 312, 11,805 bytes
music-64k.opus: 48000 Hz, 2 ch, 32.000 s
voice-16k.opus: 48000 Hz, 1 ch, 6.025 s

Settings

  • OpusEncoderSettings.BitrateBps takes 500 to 300,000 bits per second per channel, or OpusEncoderSettings.AutoBitrate (the default) or MaxBitrate. BitrateMode is Vbr (the default), ConstrainedVbr or Cbr.
  • Application is Audio for music and general sound, Voip for speech, or RestrictedLowDelay. Complexity runs from 0 to 10, and the default is 10.
  • The encoder picks SILK, hybrid or CELT for each packet, the way the reference encoder does. Bandwidth and MaxBandwidth pin the bandwidth. InBandFec with a PacketLossPercent above 0 adds forward error correction, and Dtx makes silence cost one byte per packet.
  • Input that isn’t at 48 kHz is resampled by the file writer, which is why voice-16k.opus has about three times as many frames as the 16 kHz source. The header records the original rate.
  • OpusFileWriterSettings takes 48 kHz stereo input unless you set InputSampleRate and Channels. Audio.Transcode sets both from the source. FrameMilliseconds sets the packet length: 2.5, 5, 10, 20 (the default), 40 or 60 ms.

Good to know

  • With the same settings and 16-bit input, the packets are byte-identical to the RFC 6716 reference encoder on all 269 configurations the project tested. The same input gives the same bytes on a platform.
  • More than two channels are written as channel mapping family 1, up to eight channels, in WAV speaker order.
  • OpusEncoder alone encodes raw packets of 2.5 to 60 ms at 48 kHz, for a network stream. See Decode Opus for a round trip through it.
  • Encoding allocates nothing after the encoder is built.

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