Skip to content

Instantly share code, notes, and snippets.

@Yoticc
Created August 29, 2023 07:23
Show Gist options
  • Save Yoticc/a5494dae5930442f65c9ee1a1bd90447 to your computer and use it in GitHub Desktop.
Save Yoticc/a5494dae5930442f65c9ee1a1bd90447 to your computer and use it in GitHub Desktop.
WAVWriter
using System.Runtime.InteropServices;
/* Example start */
var data = new short[240000];
for (var i = 0; i < data.Length; i++)
data[i] = (short)((i % short.MaxValue) * 20);
var path = @"C:\a.wav";
var bytes = WAV.GetBytes(ChannelType.Mono, 44000, data);
File.WriteAllBytes(path, bytes);
/* Example end */
public unsafe class WAV
{
public static byte[] GetBytes<T>(ChannelType channelType, int frequency, T[] data) where T : unmanaged
{
var bitsPerSample = (ushort)(sizeof(T) * 8);
var channels = (ushort)channelType;
var dataChunk = new DataChunk((uint)(data.Length * channels * bitsPerSample / 8));
var fmtChunk = new FMTChunk(channelType, frequency, bitsPerSample);
var riffChunk = new RIFFChunk(36 + dataChunk.SubChunk2Size);
var headers = new WAVHeaders(riffChunk, fmtChunk, dataChunk);
var headersSize = sizeof(WAVHeaders);
var dataTypeSize = sizeof(T);
var dataSize = data.Length * dataTypeSize;
byte[] bytes = new byte[headersSize + dataSize];
fixed (byte* bytesPtr = bytes)
fixed (T* dataPtr = data)
{
*(WAVHeaders*)bytesPtr = headers;
var bytesCastedPtr = (T*)(bytesPtr + headersSize);
for (int i = 0; i < data.Length; i++)
bytesCastedPtr[i] = dataPtr[i];
}
return bytes;
}
}
public enum ChannelType : ushort
{
Mono = 1,
Stereo = 2
}
[StructLayout(LayoutKind.Sequential, Size = 12, Pack = 1)]
public unsafe struct RIFFChunk
{
static int idConst;
static int formatConst;
static RIFFChunk()
{
fixed (byte* ptr = new byte[] { (byte)'R', (byte)'I', (byte)'F', (byte)'F' })
idConst = *(int*)ptr;
fixed (byte* ptr = new byte[] { (byte)'W', (byte)'A', (byte)'V', (byte)'E' })
formatConst = *(int*)ptr;
}
public RIFFChunk(uint size)
{
Size = size;
}
int ID = idConst;
public uint Size;
int Format = formatConst;
}
[StructLayout(LayoutKind.Sequential, Size = 24, Pack = 1)]
public unsafe struct FMTChunk
{
static int fmtConst;
static FMTChunk()
{
fixed (byte* ptr = new byte[] { (byte)'f', (byte)'m', (byte)'t', (byte)' ' })
fmtConst = *(int*)ptr;
}
public FMTChunk(ChannelType channels, int frequency, ushort bitsPerSample) : this((ushort)channels, frequency, bitsPerSample) { }
FMTChunk(ushort channels, int frequency, ushort bitsPerSample)
{
NumChannels = channels;
SampleRate = frequency;
ByteRate = (uint)(frequency * channels * bitsPerSample / 8);
BlockAlign = (ushort)(channels * bitsPerSample / 8);
BitsPerSample = bitsPerSample;
}
int Subchunk1ID = fmtConst;
int Subchunk1Size = 16;
ushort AudioFormat = 1;
public ushort NumChannels;
public int SampleRate;
public uint ByteRate;
public ushort BlockAlign;
public ushort BitsPerSample;
}
[StructLayout(LayoutKind.Sequential, Size = 8, Pack = 1)]
public unsafe struct DataChunk
{
static int dataConst;
static DataChunk()
{
fixed (byte* ptr = new byte[] { (byte)'d', (byte)'a', (byte)'t', (byte)'a' })
dataConst = *(int*)ptr;
}
public DataChunk(uint size)
{
SubChunk2Size = size;
}
int Subchunk2ID = dataConst;
public uint SubChunk2Size;
}
[StructLayout(LayoutKind.Sequential, Size = 44, Pack = 1)]
public unsafe record struct WAVHeaders(RIFFChunk RIFFChunk, FMTChunk FMTChunk, DataChunk DataChunk);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment