---
title: "Play an audio file"
url: https://recursiveai.org/oneaudio/cookbook/play-audio-file/
description: "Play a WAV, MP3, Ogg or FLAC file on the default sound card in C#, with oneaudio’s mixer and the RecursiveAI.OneAudio.Devices package, and check from the…"
version: "1.2.1"
---

# Play an audio file

Play a WAV, MP3, Ogg or FLAC file on the default sound card in C#, with oneaudio’s mixer and the `RecursiveAI.OneAudio.Devices` package, and check from the device’s own counters that it played.

```sh
dotnet add package RecursiveAI.OneAudio --version 1.2.1
dotnet add package RecursiveAI.OneAudio.Devices --version 1.2.1
```

```csharp
using OneAudio;
using OneAudio.Devices;

// A mixer is the source; Audio.Play opens the default output and starts it.
AudioMixer mixer = Audio.CreateMixer();           // 48 kHz stereo
using AudioOutputDevice output = Audio.Play(mixer);

AudioClip clip = Audio.Load("voice.wav");         // 16 kHz mono; the mixer converts
mixer.Play(clip);
long frames = clip.Frames * mixer.SampleRate / clip.Format.SampleRate;

// Wait until the device's own clock says the whole clip has played.
var timeout = DateTime.UtcNow.AddSeconds(15);
while (output.Counters.FramesPlayed < frames && DateTime.UtcNow < timeout)
    Thread.Sleep(50);

DeviceCounters c = output.Counters;
AudioDeviceFormat device = output.DeviceFormat;
double rate = mixer.SampleRate;
Console.WriteLine($"{output.BackendName}: {output.Device?.Id ?? "no device"}, " +
                  $"{output.State}");
Console.WriteLine($"device format {device.SampleRate} Hz, {device.Channels} ch, " +
                  $"latency {c.LatencyMilliseconds:F0} ms");
Console.WriteLine($"clip {frames / rate:F2} s, " +
                  $"device played {c.FramesPlayed / rate:F2} s, " +
                  $"peak {20 * Math.Log10(c.PeakPlayed):F1} dBFS, " +
                  $"underruns {c.Underruns}");
if (output.LastError is not null)
    Console.WriteLine($"last error: {output.LastError}");
```

```text
alsa: default, Running
device format 48000 Hz, 2 ch, latency 25 ms
clip 6.03 s, device played 6.06 s, peak -7.5 dBFS, underruns 0
```

This ran on Linux, through ALSA’s `default` device to PipeWire, with the PipeWire sink muted so the run made no sound. The stream itself ran as usual.

- `Audio.Play(mixer)` opens the default output at the mixer’s rate and channel count and starts pulling from `Render` on its own thread. Any voice the mixer plays is heard, so a game keeps one output and calls `mixer.Play` for each sound.
- `FramesPlayed` comes from the device’s clock, not from what was handed over, so it’s the right thing to wait on. It runs a little past the clip because the device keeps playing silence. `PeakPlayed` is the loudest sample among frames the device has finished: 0 means it only played silence.
- `DeviceFormat` is what the hardware runs at. When it differs from the mixer’s rate, the package converts with the library’s polyphase resampler.
- The output limiter leaves samples up to 0.9 alone and bends anything louder smoothly under 1, so a hot mix doesn’t clip.
- When no device can be opened, `State` is `NoDevice`, the output renders silently at real-time pace, retries twice a second, and `LastError` says why. The program above then stops at its timeout and prints the error.
- `using` disposes the output, which stops and closes the device. `AudioMixer` isn’t disposable.

For a long file, stream it instead of loading it: `mixer.PlayStream("music.ogg")` decodes ahead on a background thread. To play on a device other than the default, see [List audio devices](https://recursiveai.org/oneaudio/cookbook/list-audio-devices/).

MP3, WAV and Ogg playback on Windows, Linux and macOS, compared with SoundPlayer and NAudio: [Play audio in C#](https://recursiveai.org/oneaudio/articles/play-audio-csharp/).
