Recursive AI

Read a WAV file in C#

All oneaudio recipes

Audio.Open reads a WAV file and tells you how it’s stored; Read gives the samples as floats and ReadInteger as the exact stored integers. The WAV here is a tiny 24-bit stereo test file from the oneaudio repository, 64 frames long; pass your own file to the program to read it instead.

Terminal
dotnet new console -o ReadWav && cd ReadWav
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 test.wav $t/wav/pcm24_ext_stereo.wav

Put this in Program.cs and run dotnet run (or dotnet run -- yourfile.wav):

C#
using OneAudio;
 
string path = args.Length > 0 ? args[0] : "test.wav";
 
using IAudioDecoder wav = Audio.Open(path);
AudioFormat f = wav.Format;
Console.WriteLine($"{path}: {f.SampleRate} Hz, {f.Channels} ch, {f.NativeType}, " +
                  $"{f.BitsPerSample} bits, {wav.LengthFrames} frames " +
                  $"({wav.LengthFrames / (double)f.SampleRate:F3} s)");
 
// The exact stored values: 24-bit samples range from -8388608 to 8388607.
int[] ints = new int[4 * f.Channels];
wav.ReadInteger(ints);
Console.WriteLine($"first frames as integers: {string.Join(", ", ints)}");
 
// The same samples as floats, scaled so full scale is -1 to 1.
float[] floats = new float[4 * f.Channels];
wav.Seek(0);
wav.Read(floats);
IEnumerable<string> shown = floats.Select(x => x.ToString("F6"));
Console.WriteLine($"as floats: {string.Join(", ", shown)}");
 
// The whole file in memory, frame by frame: Samples[frame * channels + channel].
AudioClip clip = Audio.Load(path);
float peak = 0;
foreach (float s in clip.Samples.Span)
    peak = Math.Max(peak, Math.Abs(s));
Console.WriteLine($"peak {peak:F4} ({20 * Math.Log10(peak):F1} dBFS)");
Text
test.wav: 8000 Hz, 2 ch, PcmSigned24, 24 bits, 64 frames (0.008 s)
first frames as integers: 0, 6731767, 2709903, 7797957, 5099391, 7942129, 6885936, 7147235
as floats: 0.000000, 0.802489, 0.323046, 0.929589, 0.607895, 0.946776, 0.820868, 0.852017
peak 0.9537 (-0.4 dBFS)

What it reads

  • PCM at 8, 16, 24 and 32 bits, IEEE float at 32 and 64 bits, WAVE_FORMAT_EXTENSIBLE and RF64 (WAV over 4 GB), all with the same Audio.Open.
  • NativeType says how the samples are stored: PcmUnsigned8, PcmSigned16, PcmSigned24, PcmSigned32, IeeeFloat32 or IeeeFloat64.
  • ReadInteger gives the stored integers exactly; 8-bit samples are re-centered to −128..127. Read gives floats, never clipped.
  • A header whose sizes say “unknown” (0xFFFFFFFF), as written to a pipe, is read to the end of the stream.
  • Audio.Open also takes a Stream, so a WAV in memory or from the network reads the same way.

Limits

  • Compressed WAV formats (ADPCM, µ-law, A-law) throw UnsupportedAudioFormatException.
  • Cue points, LIST info tags and other chunks aren’t returned.

In the project’s published benchmarks, decoding 60 seconds of 16-bit stereo at 48 kHz took 3.48 ms and allocated 8 KB. To write WAV files, see Write a WAV file.