using System; using System.IO; using System.Text; //------------------------------------------------------------------------------------------------------------------- // Converts 32 bit Integer PCM to 24 bit PCM WAV 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 11/16/2006, 12/09/2007 // ------------------------------------------------------------------------------------------------------------------ class conv32to24 { const int WAVE_FORMAT_PCM = 0x0001; const int WAVE_FORMAT_EXTENSIBLE = 0xFFFE; public static void Main(String[] args) { Console.WriteLine("\n***** 32 bit to 24 bit RIFF/WAV file converter *****\n"); Console.WriteLine("Enter 32 bit WAV filename:"); String fname = Console.ReadLine(); FileInfo finfo = new FileInfo(fname); if(!finfo.Exists) { Console.WriteLine("File not found"); return; } String fileOutName = "conv24_" + 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; // 8 bytes per block (32 bit); 6 bytes per block (24 bit) short nBitsPerSample = 0; // 24 bits per sample per channel 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 WAV file format supported by this 32 to 24 bit 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") ; if(channels !=2 || nBitsPerSample !=32 || nBlockAlign !=8 || (datasize % 8) !=0) { Console.WriteLine("\nThis is not a 2 channel 32 bit WAV file"); Console.WriteLine("Channels: {0} nBitsPerSample: {1} nBlockAlign: {2} wavdata size: {3} bytes", channels, nBitsPerSample, nBlockAlign, datasize); return; } Console.WriteLine("\n32 bit {0} Hz WAV data size {1} bytes\n", nSamplesPerSec, datasize); //------- Set new required header fields from 32 bit (4 byte) to 24 bit (3 byte) samples ----------------- datasize = 3*datasize/4; // take 3 of 4 bytes for each sample; do the same for each channel riffsize = 44 - 8 + datasize; // new value of RIFF chunk size (4 bytes following "RIFF") fmtsize = 16; wFormatTag = WAVE_FORMAT_PCM; nBlockAlign = 6; nBitsPerSample = 24; nAvgBytesPerSec = nSamplesPerSec*channels*3; //3 bytes per channel (6 total for 2 channels) byte[] wavdata24 = new byte[datasize] ; int offset = 1; for (int j = 0; j