Read WAV and RF64 files in C# with oneaudio as exact integers or floats, and write 16-bit, 24-bit or 32-bit float WAV, with dither when you reduce the bit depth.
using OneAudio; // Read a WAV file: the format says how the samples are stored. using (IAudioDecoder wav = Audio.Open("music.wav")) { AudioFormat f = wav.Format; Console.WriteLine($"music.wav: {f.SampleRate} Hz, {f.Channels} ch, {f.NativeType}, " + $"{f.BitsPerSample} bits, {wav.LengthFrames} frames"); int[] ints = new int[8 * f.Channels]; wav.ReadInteger(ints); // the exact 16-bit values float[] floats = new float[8 * f.Channels]; wav.Seek(0); wav.Read(floats); // the same samples scaled to -1..1 Console.WriteLine($"sample 4: {ints[4]} as an integer, {floats[4]:F6} as a float " + $"({ints[4] / 32768.0:F6})"); } // Write floats as 24-bit PCM. AudioClip clip = Audio.Load("music.wav"); AudioFormat src = clip.Format; var format24 = new AudioFormat(src.SampleRate, src.Channels, src.ChannelMask, NativeSampleType.PcmSigned24, 24); using (var encoder = new WavEncoder(File.Create("music-24.wav"), format24, new WavEncoderSettings(), leaveOpen: false)) encoder.Write(clip.Samples.Span); // Dispose finishes the file using (IAudioDecoder check = Audio.Open("music-24.wav")) Console.WriteLine($"music-24.wav: {check.Format.NativeType}, {check.LengthFrames} frames, " + $"{new FileInfo("music-24.wav").Length:N0} bytes"); // Back to 16-bit with triangular dither, or to 32-bit float. Audio.Transcode("music-24.wav", "music-16-dither.wav", new TranscodeOptions { Wav = new WavEncoderSettings { SampleType = NativeSampleType.PcmSigned16, Dither = true }, }); Audio.Transcode("music.wav", "music-float.wav", new TranscodeOptions { Wav = new WavEncoderSettings { SampleType = NativeSampleType.IeeeFloat32 }, }); foreach (string name in new[] { "music-16-dither.wav", "music-float.wav" }) { using IAudioDecoder d = Audio.Open(name); Console.WriteLine($"{name}: {d.Format.NativeType}, {new FileInfo(name).Length:N0} bytes"); }
music.wav: 44100 Hz, 2 ch, PcmSigned16, 16 bits, 1411200 frames sample 4: 803 as an integer, 0.024506 as a float (0.024506) music-24.wav: PcmSigned24, 1411200 frames, 8,467,304 bytes music-16-dither.wav: PcmSigned16, 5,644,880 bytes music-float.wav: IeeeFloat32, 11,289,716 bytes
Reading
- PCM at 8, 16, 24 and 32 bits, IEEE float at 32 and 64 bits,
WAVE_FORMAT_EXTENSIBLEand RF64 (WAV over 4 GiB) all open withAudio.Open. Readgives floats with full scale at ±1.ReadIntegergives the stored integers exactly. 8-bit samples are re-centered to −128..127.- A WAV header whose sizes say “unknown” (0xFFFFFFFF), as written to a pipe, is read to the end of the stream.
Writing
WavEncoderSettings.SampleTypepicks the stored type. By default it’s the format’s own type, or 16-bit PCM when the source is lossy.- Floats are scaled, rounded to nearest and clipped to the integer range. NaN becomes 0.
Dither = trueadds one LSB of triangular dither first, from a fixed seed (DitherSeed), so the same input gives the same file. WAVE_FORMAT_EXTENSIBLEwith a channel mask is used for more than two channels or more than 16 bits. Float files get afactchunk.- On a seekable stream,
Finishwrites the final sizes, and a file that outgrows 4 GiB is turned into RF64. On a stream that can’t seek, giveTotalFramesfor exact sizes.ForceRf64writes RF64 from the start. WavEncoder.Encode(decoder, stream)copies a whole decoder, andAudio.Transcode(input, "out.wav")does the same from a path.
16-bit, 24-bit, float and RF64 files: Read and write WAV files in C#.