Recursive AI

Read and write FLAC in C#

All oneaudio recipes

Decode FLAC files bit-exact in C# with oneaudio, verify the MD5 signature, read the tags, and encode WAV to FLAC at compression levels 0 to 8 with your own tags.

C#
using OneAudio;
using OneAudio.Flac;
 
// Read: FlacDecoder adds the true bit depth, the tags and the MD5 check.
using (var flac = (FlacDecoder)Audio.Open("music.flac"))
{
    Console.WriteLine($"music.flac: {flac.Format.SampleRate} Hz, {flac.Format.Channels} ch, " +
                      $"{flac.BitsPerSample}-bit, {flac.LengthFrames} frames");
    Console.WriteLine($"tags: {string.Join(", ", flac.Comments)}");
 
    // Decodes the whole file; the read position is restored afterwards.
    Console.WriteLine($"MD5 matches STREAMINFO: {flac.VerifyMd5()}");
 
    // Exact integer samples, for lossless work.
    int[] ints = new int[4096 * flac.Format.Channels];
    int frames = flac.ReadInteger(ints);
    int max = ints.Take(frames * 2).Max(Math.Abs);
    Console.WriteLine($"first {frames} frames as integers, max {max}");
}
 
// Write: WAV to FLAC at the highest compression level, with tags.
long written;
using (IAudioDecoder source = Audio.Open("music.wav"))
using (FileStream output = File.Create("music-level8.flac"))
{
    written = FlacEncoder.Encode(source, output, new FlacEncoderSettings
    {
        CompressionLevel = 8,           // 0 (fastest) to 8 (smallest), default 5
        Comments = ["TITLE=Four chords", "ARTIST=Test signal"],
    });
}
long flacBytes = new FileInfo("music-level8.flac").Length;
long wavBytes = new FileInfo("music.wav").Length;
Console.WriteLine($"music-level8.flac: {written} frames, {flacBytes:N0} bytes " +
                  $"(WAV: {wavBytes:N0})");
 
// Check the round trip: the decoded integers equal the WAV's.
using IAudioDecoder a = Audio.Open("music.wav"), b = Audio.Open("music-level8.flac");
int[] x = new int[8192], y = new int[8192];
bool same = true;
int n;
while ((n = a.ReadInteger(x)) > 0)
    same &= b.ReadInteger(y) == n && x.AsSpan(0, n * 2).SequenceEqual(y.AsSpan(0, n * 2));
Console.WriteLine($"bit-exact: {same}");
Text
music.flac: 44100 Hz, 2 ch, 16-bit, 1411200 frames
tags: DATE=2026, TITLE=Four chords, ARTIST=Test signal, encoder=Lavf62.3.100
MD5 matches STREAMINFO: True
first 4096 frames as integers, max 8423
music-level8.flac: 1411200 frames, 788,714 bytes (WAV: 5,644,878)
bit-exact: True

Reading

  • ReadInteger returns the stream’s exact samples. A depth that isn’t a whole number of bytes is shifted up to the container width in Format.BitsPerSample, so a 12-bit stream reads as 16-bit values. FlacDecoder.BitsPerSample is the true depth. Read returns floats scaled to the stream’s own depth.
  • VerifyMd5 decodes the whole file and compares it with the signature in STREAMINFO. It returns null when the encoder didn’t store one, and it puts the read position back where it was.
  • Every frame is CRC-checked. A frame that contradicts STREAMINFO is rejected with AudioFormatException, after the frames before it have been delivered.
  • Ogg FLAC opens the same way as native FLAC.

Writing

  • CompressionLevel runs from 0 (fastest) to 8 (smallest), and the default is 5. Output stays within FLAC’s streamable subset unless you set Subset = false.
  • On a seekable stream, Finish (or Dispose) goes back and writes the length, the MD5 signature and a seek table. On a stream that can’t seek, give TotalFrames if you know it. There’s no MD5 or seek table.
  • BitsPerSample 0, the default, keeps the integer depth of the input format, or writes 16 bits for float and lossy input. Float samples are rounded to that depth, and Dither = true adds triangular dither first.
  • The encoder writes native FLAC only, not Ogg FLAC.
  • The project tests the encoder’s output against the reference flac -d: it decodes bit-exact, and files are within 0.4% of the size flac produces at the same level.

To convert a whole folder or other formats, see Convert audio files. Every encoder setting is in the API overview.

Decoding, encoding, tags and the MD5 check: Read and write FLAC in C#.