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.
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}");
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
ReadIntegerreturns the stream’s exact samples. A depth that isn’t a whole number of bytes is shifted up to the container width inFormat.BitsPerSample, so a 12-bit stream reads as 16-bit values.FlacDecoder.BitsPerSampleis the true depth.Readreturns floats scaled to the stream’s own depth.VerifyMd5decodes 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
CompressionLevelruns from 0 (fastest) to 8 (smallest), and the default is 5. Output stays within FLAC’s streamable subset unless you setSubset = false.- On a seekable stream,
Finish(orDispose) goes back and writes the length, the MD5 signature and a seek table. On a stream that can’t seek, giveTotalFramesif you know it. There’s no MD5 or seek table. BitsPerSample0, 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, andDither = trueadds 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 sizeflacproduces 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#.