Create a WavEncoder over a file, Write interleaved float samples, and dispose it: the encoder converts to the bit depth you choose and fills in the header. The program needs no input: it generates two seconds of a 440 Hz tone and writes it three ways.
dotnet new console -o WriteWav && cd WriteWav dotnet add package RecursiveAI.OneAudio --version 1.4.0
Put this in Program.cs and run dotnet run:
using OneAudio; // Two seconds of a 440 Hz sine at half scale, 48 kHz stereo, interleaved: // samples[frame * 2] is the left channel, samples[frame * 2 + 1] the right. const int Rate = 48000, Channels = 2; float[] samples = new float[Rate * 2 * Channels]; for (int frame = 0; frame < Rate * 2; frame++) { float s = 0.5f * MathF.Sin(2 * MathF.PI * 440 * frame / Rate); samples[frame * 2] = s; samples[frame * 2 + 1] = s; } var format = new AudioFormat(Rate, Channels); Write("tone-16.wav", NativeSampleType.PcmSigned16); Write("tone-24.wav", NativeSampleType.PcmSigned24); Write("tone-float.wav", NativeSampleType.IeeeFloat32); void Write(string path, NativeSampleType type) { var settings = new WavEncoderSettings { SampleType = type }; using (var wav = new WavEncoder(File.Create(path), format, settings, leaveOpen: false)) { wav.Write(samples); // call as often as you like } // Dispose finishes the header using IAudioDecoder check = Audio.Open(path); long bytes = new FileInfo(path).Length; Console.WriteLine($"{path,-15}{check.Format.NativeType,-13}" + $"{check.LengthFrames} frames, {bytes,9:N0} bytes"); }
tone-16.wav PcmSigned16 96000 frames, 384,080 bytes tone-24.wav PcmSigned24 96000 frames, 576,104 bytes tone-float.wav IeeeFloat32 96000 frames, 768,116 bytes
Each file plays in any audio player and opens in any editor. To write audio you produce over time, a recording or a synthesizer, keep the encoder open and call Write with each block.
Options
SampleTypepicks the stored type:PcmUnsigned8,PcmSigned16,PcmSigned24,PcmSigned32,IeeeFloat32orIeeeFloat64. Without it the encoder uses the format’s own type.- Floats are scaled, rounded to nearest and clipped to the integer range; NaN becomes 0.
Dither = trueadds one bit of triangular dither first, from a fixed seed (DitherSeed), so the same input gives the same file. Use it when you reduce 24-bit or float audio to 16 bits. WriteIntegerwrites integers exactly, for audio that is already 16 or 24-bit.WAVE_FORMAT_EXTENSIBLEwith a channel mask is used for more than two channels or more than 16 bits. Float files get afactchunk.- To copy a whole file,
Audio.Transcode("in.mp3", "out.wav")decodes and writes in one call;TranscodeOptions.Wavtakes the same settings.
Limits
- On a stream that can seek, disposing writes the final sizes, and a file that outgrows 4 GB becomes RF64 by itself.
ForceRf64writes RF64 from the start. - On a stream that can’t seek, such as a pipe or an HTTP response, the header can’t be patched afterwards. Give
TotalFramesfor exact sizes; otherwise the sizes are written as “unknown”, which oneaudio and most readers take to mean “to the end of the stream”. - The encoder isn’t thread-safe: write from one thread.
In the project’s published benchmarks, writing 60 seconds of 48 kHz stereo as 16-bit PCM took 2.8 ms. To read the files back, see Read a WAV file.