---
title: "Migrating from NAudio"
url: https://recursiveai.org/oneaudio/articles/migrate-from-naudio/
description: "Move NAudio code to OneAudio: AudioFileReader, WdlResamplingSampleProvider, WaveFileWriter, FLAC and Opus encoding, and AlsaOut or WasapiPlayer playback."
date: 2026-10-26
author: OneAudio team
---

# Migrating from NAudio

Move NAudio code to OneAudio: AudioFileReader, WdlResamplingSampleProvider, WaveFileWriter, FLAC and Opus encoding, and AlsaOut or WasapiPlayer playback.

October 26, 2026 · OneAudio team · Updated · October 27, 2026

This guide moves an NAudio app to OneAudio: reading audio files, resampling, writing WAV, FLAC and Opus, and playing sound on a device. Each NAudio sample follows NAudio’s own documentation, and each pair was compiled and run on Linux against NAudio 3.3.0 and OneAudio 1.2.1. OneAudio decodes MP3, Ogg Vorbis, Opus and FLAC in managed code, so this part of an app no longer depends on Media Foundation, ACM or a system libsndfile. Playback and recording can move as well, to the companion package `RecursiveAI.OneAudio.Devices` (WASAPI, Core Audio and ALSA), or stay with NAudio through a small adapter. The [last section](#playback) shows both.

## When moving makes sense

- You decode MP3, FLAC, Ogg Vorbis or Opus on Linux or macOS. In NAudio 3.3.0, `AudioFileReader` reads WAV and AIFF everywhere but compressed formats only on Windows, through Media Foundation and ACM, per its [package README](https://www.nuget.org/packages/NAudio/3.3.0). Off Windows it points you to `NAudio.SoundFile`, which calls libsndfile installed on the machine.
- You want one managed assembly that publishes with NativeAOT and trimming, with nothing to install on servers or containers.
- You need FLAC or Opus encoding, EBU R 128 loudness, or a mixer whose render call allocates nothing.
- You want one playback and recording API on Windows, Linux and macOS, instead of a different player and reader per platform.

Stay with NAudio for ASIO and WinMM devices, MIDI, VST 3 hosting, the SoundFont sampler, AIFF files, and MP3, AAC or WMA encoding through Media Foundation. None of these are in OneAudio 1.2.1.

## API map

| NAudio 3 | OneAudio 1.2.1 |
|---|---|
| `new AudioFileReader(path)`, `MediaFoundationReader`, `Mp3FileReader`, `SoundFileReader` | `Audio.Open(path)` to stream, `Audio.Load(path)` to decode it all |
| `ISampleProvider.Read(Span<float>)`, returns samples | `IAudioDecoder.Read(Span<float>)`, returns frames (samples ÷ channels) |
| `reader.WaveFormat` | `decoder.Format` (`SampleRate`, `Channels`, `NativeType`) |
| `reader.TotalTime` | `decoder.LengthFrames` (−1 when unknown) |
| Setting `reader.Position` or `CurrentTime` | `decoder.Seek(frame)`, sample-accurate |
| `WdlResamplingSampleProvider`, `MediaFoundationResampler` | `Audio.Resample`, `AudioResampler.Create`, or `TranscodeOptions.SampleRate` |
| `WaveFileWriter.CreateWaveFile16(path, provider)` | `Audio.Transcode(input, "out.wav")`, or `WavEncoder` for samples you produce |
| `SoundFileWriter` (FLAC, Opus), `MediaFoundationEncoder` (FLAC) | `Audio.Transcode(input, "out.flac")` or `"out.opus"`; `FlacEncoder`, `OpusFileWriter` |
| `MediaFoundationEncoder.EncodeToMp3`, `EncodeToAac`, `EncodeToWma` | No equivalent |
| `WasapiPlayer`, `WasapiOut`, `AlsaOut`: `Init(provider)`, `Play()` | `Audio.Play(mixer)`, or `Audio.CreateOutput(source)` for any `IAudioSource` or render callback (`RecursiveAI.OneAudio.Devices`, see [below](#playback)) |
| `PlaybackState`, `PlaybackStopped` | The mixer’s `VoiceEnded` event (`mixer.TryGetEvent`); `output.Counters` for what the device played |
| `WasapiRecorder`, `AlsaIn` with `DataAvailable` | `Audio.CreateInput()` and `Read(Span<float>)`, or a capture callback |
| `AsioOut`, `WaveOut` (WinMM) | No equivalent; keep NAudio |

## Reading an audio file

The NAudio version reads a WAV file, which `AudioFileReader` handles on every OS:

*Program.cs · NAudio 3.3.0*

```
using NAudio.Wave;
 
using var reader = new AudioFileReader("music.wav");
WaveFormat format = reader.WaveFormat;
Console.WriteLine($"{format.SampleRate} Hz, {format.Channels} channels, {reader.TotalTime}");
 
float[] buffer = new float[4096 * format.Channels];
long samples = 0;
int read;
while ((read = reader.Read(buffer.AsSpan())) > 0)   // samples, not frames
    samples += read;
Console.WriteLine($"{samples / format.Channels} frames");
```

*Output · Console*

```
44100 Hz, 2 channels, 00:00:30
1323000 frames
```

The OneAudio version reads the MP3 of the same audio, on any OS:

*Program.cs · OneAudio 1.2.1*

```
using OneAudio;
 
using IAudioDecoder decoder = Audio.Open("music.mp3");   // WAV, MP3, Ogg Vorbis, Opus or FLAC
AudioFormat format = decoder.Format;
Console.WriteLine($"{format.SampleRate} Hz, {format.Channels} channels, {decoder.LengthFrames} frames");
 
float[] buffer = new float[4096 * format.Channels];
long frames = 0;
int read;
while ((read = decoder.Read(buffer)) > 0)   // frames, not samples
    frames += read;
Console.WriteLine($"{frames} frames");
```

*Output · Console*

```
44100 Hz, 2 channels, 1323000 frames
1323000 frames
```

- The one change that bites: NAudio’s `Read` returns a count of samples, OneAudio’s a count of frames. Multiply by `Channels` to get samples. Both fill an interleaved float buffer at ±1 full scale.
- OneAudio finds the format from the file’s bytes. A WAV, FLAC, Ogg Vorbis or Opus file goes through the same code.
- The MP3 decodes to exactly the source’s 1,323,000 frames because OneAudio trims the encoder delay and padding recorded in the LAME header. More in [decode MP3 in C#](https://recursiveai.org/oneaudio/articles/decode-mp3-csharp/).

## Resampling

NAudio’s [resampling guide](https://github.com/naudio/NAudio/blob/main/Docs/Resampling.md) recommends `WdlResamplingSampleProvider` where Media Foundation isn’t available. From the guide, with a WAV input:

*Program.cs · NAudio 3.3.0*

```
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
 
int outRate = 48000;
var inFile = "music.wav";
var outFile = "music-48k-wdl.wav";
using (var reader = new AudioFileReader(inFile))
{
    var resampler = new WdlResamplingSampleProvider(reader, outRate);
    WaveFileWriter.CreateWaveFile16(outFile, resampler);
}
 
using var check = new WaveFileReader(outFile);
Console.WriteLine($"{check.WaveFormat}, {check.SampleCount} frames");
```

*Output · Console*

```
16 bit PCM: 48000Hz 2 channels, 1440000 frames
```

*Program.cs · OneAudio 1.2.1*

```
using OneAudio;
 
// A file to 48 kHz: decoded, resampled and written block by block.
Audio.Transcode("music.wav", "music-48k.wav", new TranscodeOptions { SampleRate = 48000 });
 
// Samples in memory: interleaved floats, any channel count.
AudioClip clip = Audio.Load("music.wav");
float[] at48 = Audio.Resample(clip.Samples.ToArray(), clip.Format.Channels,
                              fromRate: 44100, toRate: 48000, ResamplerQuality.Polyphase);
Console.WriteLine($"{clip.Frames} frames -> {at48.Length / clip.Format.Channels} frames");
```

*Output · Console*

```
1323000 frames -> 1440000 frames
```

`Audio.Resample` returns exactly round(frames × ratio) frames with the filter delay removed. The default quality is a 144-tap polyphase filter; `PolyphaseFast` and `Linear` are cheaper. For a resampler you feed block by block, as `WdlResamplingSampleProvider` is fed, use `AudioResampler.Create`, shown in [resample audio in C#](https://recursiveai.org/oneaudio/articles/resample-audio-csharp/).

## Writing WAV, FLAC and Opus

Off Windows, NAudio encodes FLAC through `NAudio.SoundFile`, as in its [cross-platform guide](https://github.com/naudio/NAudio/blob/main/Docs/CrossPlatformAudioFilesWithSoundFile.md). This needs libsndfile installed (`apt install libsndfile1` on Debian and Ubuntu):

*Program.cs · NAudio.SoundFile 3.3.0*

```
using NAudio.SoundFile;
using NAudio.Wave;
 
using (var reader = new SoundFileReader("music.wav"))
{
    SoundFileWriter.CreateSoundFile("music.flac", reader,
        SoundFileMajorFormat.Flac,
        new SoundFileWriterOptions { CompressionLevel = 0.8 });
}
Console.WriteLine($"music.flac: {new FileInfo("music.flac").Length:N0} bytes");
```

*Output · Console*

```
music.flac: 2,157,949 bytes
```

OneAudio writes FLAC, Ogg Opus and WAV from any input it reads, with nothing to install:

*Program.cs · OneAudio 1.2.1*

```
using OneAudio;
using OneAudio.Flac;
using OneAudio.Opus;
 
// FLAC, lossless. Levels 0 to 8, like the flac command line (default 5).
Audio.Transcode("music.wav", "music.flac", new TranscodeOptions
{
    Flac = new FlacEncoderSettings { CompressionLevel = 8 },
});
 
// Ogg Opus at 128 kbit/s. Input that isn't 48 kHz is resampled on the way.
Audio.Transcode("music.wav", "music.opus", new TranscodeOptions
{
    Opus = new OpusFileWriterSettings { Encoder = new OpusEncoderSettings { BitrateBps = 128_000 } },
});
 
// 24-bit WAV from an MP3.
Audio.Transcode("music.mp3", "music-24bit.wav", new TranscodeOptions
{
    Wav = new WavEncoderSettings { SampleType = NativeSampleType.PcmSigned24 },
});
 
foreach (string f in new[] { "music.flac", "music.opus", "music-24bit.wav" })
    Console.WriteLine($"{f}: {new FileInfo(f).Length:N0} bytes");
```

*Output · Console*

```
music.flac: 2,079,705 bytes
music.opus: 479,795 bytes
music-24bit.wav: 7,938,104 bytes
```

- The FLAC file decodes to exactly the samples of `music.wav`: we compared the decoded PCM with ffmpeg. Levels 0 to 8 trade encoding time for size, as with the `flac` tool.
- Opus input that isn’t 48 kHz is resampled for you, and the file records the original rate.
- For samples your code produces rather than a file, write them with `WavEncoder` or `FlacEncoder`: `Write(ReadOnlySpan<float>)` as often as you like, then `Finish()`. The same input and settings always give the same bytes on a platform.
- There is no MP3, AAC or Vorbis encoder. If you deliver MP3 or AAC, keep `MediaFoundationEncoder` on Windows or use FFmpeg.

## Playback and recording

NAudio plays through an `IWavePlayer`: `WasapiPlayer` or `WasapiOut` on Windows, `AlsaOut` from the `NAudio.Alsa` package on Linux. This is the Linux example from NAudio’s [ALSA playback guide](https://github.com/naudio/NAudio/blob/main/Docs/PlayAudioFileLinuxAlsa.md), run as written with a WAV file:

*Program.cs · NAudio.Alsa 3.3.0 · Linux*

```
using NAudio.Wave;
using NAudio.Wave.Alsa;
 
using (var audioFile = new WaveFileReader("intro.wav"))
using (var outputDevice = new AlsaOut())          // default device
{
    outputDevice.Init(audioFile);
    outputDevice.Play();
    while (outputDevice.PlaybackState == PlaybackState.Playing)
    {
        Thread.Sleep(200);
    }
}
Console.WriteLine("done");
```

In OneAudio, a device plays a mixer, and files become voices on it. One program covers every OS: the `RecursiveAI.OneAudio.Devices` package picks WASAPI, Core Audio or ALSA itself, and the file can be any format OneAudio decodes, MP3 included, with no separate reader for each platform:

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

```
using OneAudio;
using OneAudio.Devices;
 
AudioMixer mixer = Audio.CreateMixer();                        // what the device plays
using AudioOutputDevice output = Audio.Play(mixer);            // default device; WASAPI, Core Audio or ALSA
VoiceHandle voice = mixer.Play("intro.wav");                   // any format OneAudio reads
 
// NAudio's PlaybackState loop becomes a wait for the mixer's VoiceEnded event.
bool ended = false;
while (!ended)
{
    Thread.Sleep(50);
    while (mixer.TryGetEvent(out MixerEvent e))
        ended |= e.Kind == MixerEventKind.VoiceEnded && e.VoiceId == voice.Id;
}
Console.WriteLine($"done: {output.Counters.FramesPlayed} frames played");
```

*Output · Console · Linux*

```
done: 242881 frames played
```

- `dotnet add package RecursiveAI.OneAudio.Devices` brings in the core package too. Both are managed assemblies; on Linux the device package loads the system’s libasound, the same library `NAudio.Alsa` uses.
- NAudio’s player pulls from one provider. OneAudio’s output pulls from the mixer, so starting a second sound is another `mixer.Play` call, not a `MixingSampleProvider` you build first.
- A missing device isn’t an exception: the output keeps its clock running, retries twice a second and says why in `output.LastError`. It also follows the system default device when the user changes it.
- Recording maps the same way: `Audio.CreateInput()` replaces `AlsaIn` or `WasapiRecorder` and their `DataAvailable` event, and you read float samples instead of bytes. Both are shown side by side in [record audio in C#](https://recursiveai.org/oneaudio/articles/record-audio-csharp/).

### Or keep NAudio’s player

You don’t have to move playback at all. A few lines turn a OneAudio decoder (or the OneAudio mixer’s output) into an NAudio `ISampleProvider`, which every NAudio player accepts. NAudio 3’s providers read into a `Span<float>`, per its [migration guide](https://github.com/naudio/NAudio/blob/main/Docs/MigratingFromNAudio2.md). This sample writes the provider to a file so it runs anywhere; with a device, pass the same provider to `player.Init`:

*Program.cs · OneAudio 1.2.1 + NAudio.Core 3.3.0*

```
using NAudio.Wave;
using OneAudio;
 
// Decode with OneAudio, hand the samples to anything in NAudio that takes an ISampleProvider:
// a player such as WasapiPlayer or AlsaOut (player.Init(provider)), a mixer, or a file writer.
using IAudioDecoder decoder = Audio.Open("music.mp3");
var provider = new OneAudioSampleProvider(decoder);
 
WaveFileWriter.CreateWaveFile16("bridged.wav", provider);
Console.WriteLine($"{provider.WaveFormat} -> bridged.wav, {new FileInfo("bridged.wav").Length:N0} bytes");
 
sealed class OneAudioSampleProvider(IAudioDecoder decoder) : ISampleProvider
{
    public WaveFormat WaveFormat { get; } =
        WaveFormat.CreateIeeeFloatWaveFormat(decoder.Format.SampleRate, decoder.Format.Channels);
 
    // NAudio counts samples; OneAudio counts frames (one sample per channel).
    public int Read(Span<float> buffer)
    {
        int channels = decoder.Format.Channels;
        int total = 0;
        while (total < buffer.Length)
        {
            int frames = decoder.Read(buffer[total..].Slice(0, (buffer.Length - total) / channels * channels));
            if (frames == 0) break;
            total += frames * channels;
        }
        return total;
    }
}
```

*Output · Console*

```
32 bit IEEEFloat: 44100Hz 2 channels -> bridged.wav, 5,292,044 bytes
```

The adapter converts between the two counting conventions: NAudio asks for samples, the decoder returns frames. It reads until the buffer is full, because NAudio expects a provider to fill every read until the end of the stream, as its resampling guide explains. For game audio, wrap `AudioMixer.Render` the same way and let NAudio’s player pull the mix.

## Where next

- [Getting started with OneAudio](https://recursiveai.org/oneaudio/docs/getting-started/) and the [API overview](https://recursiveai.org/oneaudio/docs/api/).
- The cookbook: [FLAC](https://recursiveai.org/oneaudio/cookbook/flac/), [encode Opus](https://recursiveai.org/oneaudio/cookbook/encode-opus/), [WAV files](https://recursiveai.org/oneaudio/cookbook/wav/) and [streaming decode](https://recursiveai.org/oneaudio/cookbook/streaming-decode/).
- [Play audio in C#](https://recursiveai.org/oneaudio/articles/play-audio-csharp/) and [record audio in C#](https://recursiveai.org/oneaudio/articles/record-audio-csharp/), with the device package in detail.
- [Choosing a C# audio library](https://recursiveai.org/oneaudio/articles/csharp-audio-library/), if you are still deciding.
