---
title: "Read and write FLAC in C#"
url: https://recursiveai.org/oneaudio/articles/flac-csharp/
description: "Decode and encode FLAC in C# on any OS with managed code: exact integer samples, MD5 verification, tags, WAV to FLAC at levels 0 to 8, and 24-bit output."
date: 2026-10-27
author: OneAudio team
---

# Read and write FLAC in C#

Decode and encode FLAC in C# on any OS with managed code: exact integer samples, MD5 verification, tags, WAV to FLAC at levels 0 to 8, and 24-bit output.

October 27, 2026 · OneAudio team

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

*Terminal · NuGet*

```
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

*Program.cs · OneAudio 1.2.1 · .NET 10*

```
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");
```

*Output · Console*

```
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.Open` detects FLAC from the bytes and returns a `FlacDecoder`, which adds `BitsPerSample` (the true depth), `Vendor`, `Comments` (the Vorbis comment tags, such as `TITLE=…`), `Md5` and `VerifyMd5()` to the common decoder interface.
- `ReadInteger` returns the samples exactly as stored; `Read` returns 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.
- `VerifyMd5` decodes the whole file and compares it with the signature in STREAMINFO, then puts the read position back. It returns `null` when 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

*Program.cs · OneAudio 1.2.1 · .NET 10*

```
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));
```

*Output · Console*

```
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 `flac` tool, and stay inside FLAC’s streamable subset. Block size, LPC order, partition order and coefficient precision can be set by hand.
- `Audio.Transcode` takes any input OneAudio reads. From a lossless source (WAV or FLAC) the output is bit-exact: ffmpeg decodes `music-l8.flac` to the same PCM as `music.wav`, byte for byte.
- `FlacEncoder` takes 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; set `Dither = true` for triangular dither first. `WriteInteger` writes integers as they are.
- On a seekable stream, `Finish` goes 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](https://www.nuget.org/packages/NAudio/3.3.0) 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:

*Program.cs · NAudio 3.3.0 · Linux*

```
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}");
}
```

*Output · Console*

```
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](https://github.com/naudio/NAudio/blob/main/Docs/CrossPlatformAudioFilesWithSoundFile.md), 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](https://recursiveai.org/oneaudio/articles/migrate-from-naudio/#playback); the device package also plays FLAC directly ([play audio in C#](https://recursiveai.org/oneaudio/articles/play-audio-csharp/)).

## Conformance and speed

From the project’s [published results](https://github.com/recursive-ai-oss/oneaudio-csharp/blob/v1.2.1/README.md#results):

- **Decoder:** bit-exact against `flac` 1.4.3 and the STREAMINFO MD5 on all 64 files of the IETF CELLAR test suite’s `subset`; the 11 `uncommon` files behave as expected, and the 11 `faulty` files are cleanly rejected.
- **Encoder:** output decoded by `flac -d` is bit-exact, and file sizes are within 0.4% of `flac`’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](https://recursiveai.org/oneaudio/cookbook/flac/) and [read audio metadata](https://recursiveai.org/oneaudio/cookbook/read-audio-metadata/), the cookbook recipes.
- The [FLAC section](https://recursiveai.org/oneaudio/docs/api/#flac) and the [encoder settings](https://recursiveai.org/oneaudio/docs/api/#encoding) in the API overview.
- [Read and write WAV files in C#](https://recursiveai.org/oneaudio/articles/wav-files-csharp/) and [encode Opus in C#](https://recursiveai.org/oneaudio/articles/opus-csharp/), for a lossy copy.
