To read a FLAC file in C#, add the RecursiveAI.OneAudio package and call Audio.Open("song.flac") to stream it or Audio.Load("song.flac") to decode it all; to write one, call Audio.Transcode("song.wav", "song.flac") or feed samples to a FlacEncoder. The decoder returns the exact integers stored in the file and checks them against the file’s MD5 signature, and the encoder writes compression levels 0 to 8 like the flac command line. It is all managed C#, so the same code runs on Windows, Linux and macOS with no libFLAC or Media Foundation. Every sample below was compiled and run against OneAudio 1.2.1 on .NET 10.
Before you start
dotnet new console -o FlacDemo cd FlacDemo dotnet add package RecursiveAI.OneAudio --version 1.2.1
OneAudio targets .NET 10 and is licensed MIT OR Apache-2.0. The samples use music.wav, 30 seconds of 16-bit stereo at 44.1 kHz, and music.flac, the same audio encoded by ffmpeg.
Read a FLAC file
using OneAudio; using OneAudio.Flac; // Open a FLAC file: the concrete decoder exposes the tags, the true bit depth and the MD5 signature. using var flac = (FlacDecoder)Audio.Open("music.flac"); Console.WriteLine($"{flac.Format.SampleRate} Hz, {flac.Format.Channels} ch, {flac.BitsPerSample}-bit, {flac.LengthFrames} frames"); Console.WriteLine($"vendor: {flac.Vendor}, tags: {flac.Comments.Count}"); Console.WriteLine($"MD5 matches: {flac.VerifyMd5()}"); // decodes the whole file and compares with STREAMINFO // Exact integers, as stored: compare with the WAV the file was made from. using IAudioDecoder wav = Audio.Open("music.wav"); int[] a = new int[4096 * 2], b = new int[4096 * 2]; long frames = 0, differences = 0; int n; while ((n = flac.ReadInteger(a)) > 0) { wav.ReadInteger(b.AsSpan(0, n * 2)); for (int i = 0; i < n * 2; i++) if (a[i] != b[i]) differences++; frames += n; } Console.WriteLine($"{frames} frames compared with music.wav, {differences} differences");
44100 Hz, 2 ch, 16-bit, 1323000 frames vendor: Lavf62.3.100, tags: 1 MD5 matches: True 1323000 frames compared with music.wav, 0 differences
Audio.Opendetects FLAC from the bytes and returns aFlacDecoder, which addsBitsPerSample(the true depth),Vendor,Comments(the Vorbis comment tags, such asTITLE=…),Md5andVerifyMd5()to the common decoder interface.ReadIntegerreturns the samples exactly as stored;Readreturns floats scaled by the stream’s own depth, so full scale is ±1 for 16-bit and 24-bit files alike. The comparison shows the round trip from WAV to FLAC and back is lossless: zero differences over 2,646,000 samples.VerifyMd5decodes the whole file and compares it with the signature in STREAMINFO, then puts the read position back. It returnsnullwhen the file stores no signature. Every frame is also CRC-checked as it is read.Seek(frame)is sample-accurate, and Ogg FLAC files open through the same call.
Write FLAC: WAV to FLAC, and samples you produce
using OneAudio; using OneAudio.Flac; // WAV to FLAC in one call, at the highest compression level, with tags. Audio.Transcode("music.wav", "music-l8.flac", new TranscodeOptions { Flac = new FlacEncoderSettings { CompressionLevel = 8, Comments = ["TITLE=Test piece", "ARTIST=OneAudio samples"], }, }); // Samples your own code produces: 24-bit FLAC written block by block. var format = new AudioFormat(48000, 2); using (FileStream file = File.Create("tone-24bit.flac")) using (var encoder = new FlacEncoder(file, format, new FlacEncoderSettings { BitsPerSample = 24 })) { float[] block = new float[1024 * 2]; for (int b = 0; b < 48000 / 1024; b++) { for (int i = 0; i < 1024; i++) block[2 * i] = block[2 * i + 1] = 0.5f * MathF.Sin(2 * MathF.PI * 1000 * (b * 1024 + i) / 48000f); encoder.Write(block); } encoder.Finish(); // writes the length, MD5 and seek table } foreach (string f in new[] { "music.wav", "music-l8.flac", "tone-24bit.flac" }) Console.WriteLine($"{f}: {new FileInfo(f).Length:N0} bytes"); using var check = (FlacDecoder)Audio.Open("music-l8.flac"); Console.WriteLine(string.Join(", ", check.Comments));
music.wav: 5,292,078 bytes music-l8.flac: 2,079,752 bytes tone-24bit.flac: 68,628 bytes TITLE=Test piece, ARTIST=OneAudio samples
- At level 8 the music file shrinks to 39% of the WAV. Levels 0 to 8 (default 5) trade encoding time for size, as with the
flactool, and stay inside FLAC’s streamable subset. Block size, LPC order, partition order and coefficient precision can be set by hand. Audio.Transcodetakes any input OneAudio reads. From a lossless source (WAV or FLAC) the output is bit-exact: ffmpeg decodesmusic-l8.flacto the same PCM asmusic.wav, byte for byte.FlacEncodertakes 4 to 32 bits per sample, 1 to 8 channels and any rate up to 1,048,575 Hz. Float input is rounded to the chosen depth; setDither = truefor triangular dither first.WriteIntegerwrites integers as they are.- On a seekable stream,
Finishgoes back and writes the length, the MD5 signature and a seek table. On a pipe or a network stream it writes STREAMINFO once, without them. - The same input and settings always produce the same bytes on a platform.
FLAC with NAudio
Many C# projects start with NAudio’s AudioFileReader. Its package README for NAudio 3.3.0 says it reads WAV and AIFF everywhere and “uses Media Foundation (MP3, AAC, WMA, FLAC…)” on Windows. On Linux, opening a FLAC file says so:
using NAudio.Wave; try { using var reader = new AudioFileReader("music.flac"); Console.WriteLine($"{reader.WaveFormat}, {reader.TotalTime}"); } catch (Exception ex) { Console.WriteLine($"{ex.GetType().Name}: {ex.Message}"); }
NotSupportedException: This file format is only supported by AudioFileReader on Windows, where it is decoded by Media Foundation or ACM. On other platforms use SoundFileReader from the NAudio.SoundFile package, or the Extended Audio File Services readers in NAudio.MacOS.The route the message points to, NAudio.SoundFile, reads and writes FLAC through a system installation of libsndfile, per its guide, so each machine needs libsndfile1 installed. If NAudio plays your audio, a OneAudio decoder can feed its player through an ISampleProvider, as shown in migrating from NAudio; the device package also plays FLAC directly (play audio in C#).
Conformance and speed
From the project’s published results:
- Decoder: bit-exact against
flac1.4.3 and the STREAMINFO MD5 on all 64 files of the IETF CELLAR test suite’ssubset; the 11uncommonfiles behave as expected, and the 11faultyfiles are cleanly rejected. - Encoder: output decoded by
flac -dis bit-exact, and file sizes are within 0.4% offlac’s at levels 0 to 8. - Decode speed: on 16-bit 44.1 kHz, 24-bit 96 kHz and 24-bit 192 kHz files, OneAudio decoded 1.36 to 1.57 times as fast as PureFlac 1.0.0 and 2.07 to 8.09 times as fast as BunLabs.NAudio.Flac 2.0.1, allocating about 97 KB per file against 10 to 129 MB for the latter. Measured with BenchmarkDotNet 0.15.8 on an AMD Ryzen 9 7950X3D under Linux with .NET 10.0.13.
Where next
- FLAC and read audio metadata, the cookbook recipes.
- The FLAC section and the encoder settings in the API overview.
- Read and write WAV files in C# and encode Opus in C#, for a lossy copy.