Created
August 29, 2023 07:23
-
-
Save Yoticc/a5494dae5930442f65c9ee1a1bd90447 to your computer and use it in GitHub Desktop.
WAVWriter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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