I recently developed and debugged this part of Wave writing, can share with you:
namespace Wave {
using System;
using System.IO;
using Encoding = System.Text.Encoding;
public partial class WaveWriter : BinaryWriter, IDisposable {
public WaveWriter(string fileName)
: base(new FileStream(fileName, FileMode.Create, FileAccess.Write)) {
Initialize(new WaveParameterWriter());
}
public WaveWriter(string fileName, uint rate, short bits, short channels)
: base(new FileStream(fileName, FileMode.Create, FileAccess.Write)) {
Initialize(new WaveParameterWriter(rate, bits, channels));
}
public WaveWriter(Stream stream) : base(stream) { Initialize(new WaveParameterWriter()); }
public WaveWriter(Stream stream, uint rate, short bits, short channels)
: base(stream) {
Initialize(new WaveParameterWriter(rate, bits, channels));
}
void Initialize(WaveParameterWriter parameterWriter) {
this.waveParameterWriter = parameterWriter;
WriteRiffHeader();
}
void WriteRiffHeader() {
Func<string, byte[]> serialize = (id) => { return Encoding.ASCII.GetBytes(id); };
Write(serialize(DefinitionSet.ChunkId));
Write(audioDataSize + DefinitionSet.ChunkHeaderSize);
Write(serialize(DefinitionSet.Format));
Write(serialize(DefinitionSet.SubChunkOneId));
Write(DefinitionSet.SubChunkOneSize);
Write(DefinitionSet.AudioFormatPcm);
waveParameterWriter.Write(this);
Write(DefinitionSet.ExtraParameterSizePcm);
Write(serialize(DefinitionSet.SubChunkTwoId));
Write(audioDataSize);
audioStreamStart = this.BaseStream.Position;
}
void IDisposable.Dispose() { Close(); }
public override void Close() {
if (isClosed) return;
audioDataSize = (uint)(this.BaseStream.Position - audioStreamStart);
Seek(0, SeekOrigin.Begin);
WriteRiffHeader();
isClosed = true;
base.Close();
}
long audioStreamStart;
WaveParameterWriter waveParameterWriter;
uint audioDataSize;
bool isClosed;
sealed class WaveParameterWriter {
internal WaveParameterWriter() : this(DefinitionSet.DefaultRate, DefinitionSet.DefaultBitsPerSample, DefinitionSet.DefaultChannels) { }
internal WaveParameterWriter(uint rate, short bits, short channels) {
this.channels = channels;
this.sampleRate = rate;
this.bitsPerSample = bits;
this.blockAlign = (short)(channels * (bits / 8));
this.byteRate = (uint)(sampleRate * blockAlign);
}
internal void Write(BinaryWriter writer) {
writer.Write(channels);
writer.Write(sampleRate);
writer.Write(byteRate);
writer.Write(blockAlign);
writer.Write(bitsPerSample);
}
short channels;
uint sampleRate;
uint byteRate;
short blockAlign;
short bitsPerSample;
}
}
}
It is based on the following definitions:
using System;
using System.Collections.Generic;
namespace Wave {
public partial class WaveWriter {
static class DefinitionSet {
internal const string ChunkId = "RIFF";
internal const uint ChunkHeaderSize = 38;
internal const string Format = "WAVE";
internal const string SubChunkOneId = "fmt ";
internal const uint SubChunkOneSize = 18;
internal const short AudioFormatPcm = 1;
internal const short ExtraParameterSizePcm = 0;
internal const string SubChunkTwoId = "data";
internal const uint DefaultRate = 44100;
internal const short DefaultBitsPerSample = 16;
internal const short DefaultChannels = 2;
}
}
}
I tested it pretty well; it works.
Primarily, I used this piece of documentation:
https://ccrma.stanford.edu/courses/422/projects/WaveFormat[
^].
—SA