using System; using System.IO; using System.Text; //------------------------------------------------------------------------------------------------------------------- // Converts PCM WAV to simplest 44 byte RIFF header (with 16 byte fmt chunk size) file // Only supports simplest RIFF WAVEFORMATEX and WAVEFORMATEXTENSIBLE header // Writes WAVEFORMATEX PCM WAV file with 16 byte format block; ignores any other subchunks ('fact', 'list' etc..) // JavaScience Consulting 12/09/2007 // ------------------------------------------------------------------------------------------------------------------ class riff44 { const int WAVE_FORMAT_PCM = 0x0001; const int WAVE_FORMAT_EXTENSIBLE = 0xFFFE; public static void Main(String[] args) { Console.WriteLine("\n***** Convert to 44 byte RIFF/WAV file converter *****\n"); Console.WriteLine("PCM WAV filename to convert:"); String fname = Console.ReadLine(); FileInfo finfo = new FileInfo(fname); if(!finfo.Exists) { Console.WriteLine("File not found"); return; } String fileOutName = "riff44_" + fname; Console.WriteLine(); ASCIIEncoding ascii = new ASCIIEncoding(); byte[] previewheader = new byte[80] ; //preview some binary header data byte[] blocktype = new byte[4]; //------- basic RIFF/WAV essential header structure ------ byte[] h1 = ascii.GetBytes("RIFF"); int riffsize = 0; byte[] h2 = ascii.GetBytes("WAVEfmt "); int fmtsize = 0; //format chunk size ushort wFormatTag = 0x01; //PCM short channels = 0; //stereo int nSamplesPerSec =0; // sampling rate int nAvgBytesPerSec = 0; // should be nSamplesPerSec*nBlockAlign short nBlockAlign = 0; short nBitsPerSample = 0; byte[] h3 = ascii.GetBytes("fact"); byte[] h4 = ascii.GetBytes("data") ; int unknownSubChunk_size = 0; //curently only support "RIFF", "fmt " and "data" chunks; skill the rest int datasize = 0; byte[] wavdata; try{ using(BinaryReader binReader = new BinaryReader(finfo.Open(FileMode.Open))) { previewheader = binReader.ReadBytes(previewheader.Length); showBytes("WAV header preview", previewheader); Console.WriteLine("\"data\" [64 61 74 61] \"fact\" [66 61 63 74]") ; //hints binReader.BaseStream.Position = 0; //reposition stream to begining if(!CompareBytearrays(h1, binReader.ReadBytes(4))) return; riffsize = binReader.ReadInt32() ; if(!CompareBytearrays(h2, binReader.ReadBytes(8))) //only support WAV RIFF file types return; fmtsize = binReader.ReadInt32() ; if(fmtsize < 16) //minimum fmt chuck size must be at least 16 bytes return; wFormatTag = binReader.ReadUInt16(); if(wFormatTag != WAVE_FORMAT_PCM && wFormatTag != WAVE_FORMAT_EXTENSIBLE) return; channels = binReader.ReadInt16(); nSamplesPerSec = binReader.ReadInt32(); nAvgBytesPerSec = binReader.ReadInt32(); nBlockAlign = binReader.ReadInt16(); nBitsPerSample = binReader.ReadInt16(); if(fmtsize !=16) // skip over the extra WAVEFORMATEXTENSIBLE data { int skipped = fmtsize-16; binReader.BaseStream.Seek(skipped, SeekOrigin.Current); Console.WriteLine("\nSkipping {0} bytes in fmt chunk .. ", skipped) ; } // ------ parse and skip any remaining chunks until we find the expected final 'data' chunk or EOF ------ while ( binReader.PeekChar() != -1 && !CompareBytearrays(h4, binReader.ReadBytes(4))) { unknownSubChunk_size = binReader.ReadInt32(); binReader.ReadBytes(unknownSubChunk_size); Console.WriteLine("\nSkipping subchunk of size {0} bytes", unknownSubChunk_size) ; } if (binReader.PeekChar() == -1) //no 'data' chunk present; bad WAV file return; datasize = binReader.ReadInt32() ; //we have the final 'data' chunk now wavdata = new byte[datasize]; wavdata = binReader.ReadBytes(datasize) ; } } catch(Exception ex){ Console.WriteLine(ex.Message); Console.WriteLine("Not a valid WAV file format supported by this converter"); return; } if( nAvgBytesPerSec != nSamplesPerSec*nBlockAlign){ Console.WriteLine("Inconsistent WAV header information\n nAvgBytesPerSec not equal to nSamplesPerSec*nBlockAlign"); return; } Console.WriteLine("\nThis appears to be a valid RIFF WAV header") ; Console.WriteLine("Channels: {0} nBitsPerSample: {1} nBlockAlign: {2} wavdata size: {3} bytes", channels, nBitsPerSample, nBlockAlign, datasize); //------- Set new required header fields to remove extra WAVEFORMATEXTENSIBLE data and extra subchunks ----------------- riffsize = 44 - 8 + datasize; // new value of RIFF chunk size (4 bytes following "RIFF") fmtsize = 16; wFormatTag = WAVE_FORMAT_PCM; using(BinaryWriter binWriter = new BinaryWriter(File.Open(fileOutName, FileMode.Create))) { binWriter.Write(h1); binWriter.Write(riffsize); binWriter.Write(h2); binWriter.Write(fmtsize); binWriter.Write(wFormatTag); binWriter.Write(channels); binWriter.Write(nSamplesPerSec); binWriter.Write(nAvgBytesPerSec); binWriter.Write(nBlockAlign); binWriter.Write(nBitsPerSample); binWriter.Write(h4); binWriter.Write(datasize); binWriter.Write(wavdata); } long outsize = (new FileInfo(fileOutName)).Length; Console.WriteLine("Converted stereo WAV file to 44 byte RIFF header stereo WAV file\n"); Console.WriteLine("Input file '{0}' {1} bytes\nOutput file '{2}' {3} bytes", fname, finfo.Length, fileOutName, outsize); } private static bool CompareBytearrays(byte [] a, byte[] b) { if(a.Length != b.Length) return false; int i =0; foreach(byte c in a) { if(c != b[i] ) return false; i++; } return true; } private static void showBytes(String info, byte[] data){ Console.WriteLine("{0} [{1} bytes]", info, data.Length); for(int i=1; i<=data.Length; i++){ Console.Write("{0:X2} ", data[i-1]) ; if(i%16 == 0) Console.WriteLine(); } } }