Recursive AI

Read a FLAC file in C#

All oneaudio recipes

Audio.Open reads a FLAC file as exact integers or floats, and the FlacDecoder it returns adds the tags and the MD5 check. It’s managed code with no libFLAC. The file here is a three-second synthetic test file from the oneaudio repository; any FLAC file works the same way.

Terminal
dotnet new console -o ReadFlac && cd ReadFlac
dotnet add package RecursiveAI.OneAudio --version 1.4.0
t=https://raw.githubusercontent.com/recursive-ai-oss/oneaudio-csharp/v1.4.0/testdata
curl -L -o song.flac $t/flac/stereo16_seektable.flac

Put this in Program.cs and run dotnet run:

C#
using OneAudio;
using OneAudio.Flac;
 
using (var flac = (FlacDecoder)Audio.Open("song.flac"))
{
    AudioFormat f = flac.Format;
    Console.WriteLine($"song.flac: {f.SampleRate} Hz, {f.Channels} ch, " +
                      $"{flac.BitsPerSample}-bit, {flac.LengthFrames} frames");
    Console.WriteLine($"vendor: {flac.Vendor}, tags: {flac.Comments.Count}");
 
    // Decodes the whole file and checks it against the signature in the header.
    Console.WriteLine($"MD5 matches: {flac.VerifyMd5()}");
 
    // The exact integer samples, for lossless work.
    int[] ints = new int[4096 * f.Channels];
    int frames = flac.ReadInteger(ints);
    int peak = ints.Take(frames * f.Channels).Max(Math.Abs);
    Console.WriteLine($"first {frames} frames as integers, peak {peak} of 32767");
}
 
// Or the whole file as floats, full scale at -1 and 1.
AudioClip clip = Audio.Load("song.flac");
Console.WriteLine($"as floats: {clip.Samples.Length} samples");
 
// FLAC to WAV: bit-exact, at the same bit depth.
TranscodeResult wav = Audio.Transcode("song.flac", "song.wav");
Console.WriteLine($"song.wav: {wav.Frames} frames, " +
                  $"{new FileInfo("song.wav").Length:N0} bytes");
Text
song.flac: 22050 Hz, 2 ch, 16-bit, 66000 frames
vendor: reference libFLAC 1.4.3 20230623, tags: 0
MD5 matches: True
first 4096 frames as integers, peak 25104 of 32767
as floats: 132000 samples
song.wav: 66000 frames, 264,080 bytes

What you get

  • ReadInteger returns the stream’s exact samples. A depth that isn’t a whole number of bytes is shifted up to the container width, so a 12-bit stream reads as 16-bit values; FlacDecoder.BitsPerSample is the true depth. Read returns floats.
  • VerifyMd5 returns null when the encoder stored no signature, and puts the read position back where it was.
  • Comments holds the Vorbis comment tags as NAME=value strings: TITLE, ARTIST and so on.
  • FLAC to WAV is bit-exact: the WAV holds the same integers at the same depth.

Limits

  • Every frame is CRC-checked. A frame that contradicts the stream header is rejected with AudioFormatException, after the frames before it have been delivered.
  • Ogg FLAC opens the same way as native FLAC. Embedded pictures (cover art) aren’t returned.
  • Bit-exact against flac 1.4.3 and the MD5 on all 64 files of the IETF CELLAR test suite’s subset; its 11 faulty files are rejected cleanly.

With NAudio

NAudio 3.3.0’s AudioFileReader reads FLAC through Media Foundation, on Windows only (package README); elsewhere NAudio.SoundFile needs a system libsndfile. In the project’s published results, oneaudio decoded FLAC 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.

To write FLAC, see Convert WAV to FLAC.