How to read a wave file using C++

By Danny Mcauliffe

This is a very small part of a program called FFTMAX Iam writing in C++ .I hope to give the reader some understanding of how to go about reading a wave file into a array .there are many classes you could use to do this for you but I think you get a better idea and more control if you write the code your self so lets have a look at how we go about this. A wave file begins with a header RIFF; after it, two subblocks are defined—FMT and DATA. The RIFF contains of three elements: RIFF_ID, RIFF_SIZE, and RIFF_FORMAT. heres the code Ive used to read the header of a wave file and output the data to a text file this is only for testing so I would not load a big file into this code as you may end up with a very big text file.once youve got the idea of how the code works you could rewrite it and make it do something more usefull as I have done with my FFTMAX project. You can see the code is quite simple.For a more in indepth look into the WaveFile format take a look at my WaveFormat page.

This first block opens a dialog so you can pick and load a wave file,it also sets up some variables.

			//Open dialog Start 
				BYTE id[4], *sound_buffer;				
				DWORD size;				
				short format_tag, channels, block_align, bits_per_sample;
				short extra_param_size;				
				DWORD format_length, sample_rate, avg_bytes_sec, data_size, i;				
				char szExtBuffer[MAX_PATH] = "";				
				OPENFILENAME ofn;				
				strcpy(szExtBuffer, "*.Wav");				
				memset(&ofn, 0, sizeof(ofn));				
				ofn.lStructSize       = sizeof(ofn);				
				ofn.lpstrFile         = szExtBuffer;				
				ofn.nMaxFile          = MAX_PATH;				
				ofn.lpstrTitle        = "Open file";				
			//Open dialog Stop  
				
This next block prints out the file name and path
  if (fp) {				
			std::ofstream WAVDATA("WAVDATA.rtf",std::ios::out);
				WAVDATA << "Opened ";
				WAVDATA << filename;
				WAVDATA << " for reading\n";
				WAVDATA.close();  

Wave file headers follow the standard RIFF file format structure. The first 8 bytes in the file is a standard RIFF chunk header which has a chunk ID of "RIFF" and a chunk size equal to the file size minus the 8 bytes used by the header. The first 4 data bytes in the "RIFF" chunk determines the type of resource found in the RIFF chunk.

fread(id, sizeof(BYTE), 4, fp); 			     
 			     if (strncmp((char *) id, "RIFF", 4) == 0) { 
 			     std::ofstream WAVDATA("WAVDATA.rtf",std::ios::app ); 
 			     WAVDATA << "Header type "; 
 			     WAVDATA << id; 
 			     WAVDATA << "\n";
 			     fread(&size, sizeof(DWORD), 1, fp);
 			     WAVDATA << "Data Size "; 
 			     WAVDATA << size;
 		         WAVDATA << "\n"; 

Wave files should always use "WAVE" after the RIFF id comes all of the Wave file chunks that define the audio waveform.

  fread(id, sizeof(BYTE), 4, fp);
 			     if (strncmp((char *) id, "WAVE", 4) == 0) { 
 			     WAVDATA << "Data Type "; 
 			     WAVDATA << id;
 			     WAVDATA << "\n";  

The format chunk contains information about how the waveform data is stored and should be played back including the type of compression used, number of channels, sample rate, bits per sample and other attributes.

 fread(id, sizeof(BYTE), 4, fp);
 			     WAVDATA << "Format header "; 
 			     WAVDATA << id;
 			     WAVDATA << "\n"; 

now we read the format chunk length.

 fread(&format_length, sizeof(DWORD), 1, fp);
 			     WAVDATA << "Format length "; 
 			     WAVDATA << format_length;
 			     WAVDATA << "\n"; 

The chunk ID is always "fmt " (0x666D7420) and the size is the size of the standard wave format data (16 bytes) plus the size of any extra format bytes needed for the specific Wave format, if it does not contain uncompressed PCM data. Note the chunk ID string ends with the space character (0x20).

fread(&format_tag, sizeof(short), 1, fp);
 			     WAVDATA << "Format Tag "; 
 			     WAVDATA << format_tag;
 			     WAVDATA << "\n"; 


The number of channels specifies how many separate audio signals that are encoded in the wave data chunk. A value of 1 means a mono signal, a value of 2 means a stereo signal and so on.

 fread channels, sizeof(short), 1, fp);
 			     WAVDATA << "Number of Channels "; 
 			     WAVDATA << channels;
 			     WAVDATA << "\n";

Now we read the sample rate, this can be anything from 8K up to 96K but most of the time it will be 44.1 , Cd audio is set at 44.1 K.

 fread(&sample_rate, sizeof(DWORD), 1, fp);
 			     WAVDATA << "Sample rate "; 
 			     WAVDATA << sample_rate;
 			     WAVDATA << "\n";  

Average Bytes Per Second
This value indicates how many bytes of wave data must be streamed to a D/A converter per second in order to play the wave file. This information is useful when determining if data can be streamed from the source fast enough to keep up with playback. This value can be easily calculated with the formula: AvgBytesPerSec=SampleRate*BlockAlign.

fread(&avg_bytes_sec, sizeof(DWORD), 1, fp);
 			     WAVDATA << "Avg bytes sec "; 
 			     WAVDATA << avg_bytes_sec;
 			     WAVDATA << "\n";  

Block Align
This value is not affected by the number of channels and can be calculated with the formula: BlockAlign = SignificantBitsPerSample/8* NumChannels.

 fread(&block_align, sizeof(short), 1, fp); 
 			     WAVDATA << "Block align "; 
 			     WAVDATA << block_align;
 			     WAVDATA << "\n"; 

Significant Bits Per Sample
This value specifies the number of bits used to define each sample. This value is usually 8, 16, 24 or 32. If the number of bits is not byte aligned (a multiple of 8) then the number of bytes used per sample is rounded up to the nearest byte size and the unused bytes are set to 0.

fread(&bits_per_sample, sizeof(short), 1, fp);
 			     WAVDATA << "Bits per sample "; 
 			     WAVDATA << bits_per_sample;
 			     WAVDATA << "\n";
 			     WAVDATA.close(); 

The last bit of this code sorts out the wave data into a list .as you can see there are two bytes for each channel so it adds these together and prints the result again this is just for testing and to make the data easier to read.so the result is one line labelled DATA WORD followed by two numbers being the data for the left then right channels

 if (format_tag != 1) { 
 			     fread (&extra_param_size, sizeof(short), 1, fp); 
 			     for (i = 0; i < extra_param_size; i++)
 			     {
 			     fread(id, 1, 1, fp);
 			     }
 			     /* You could try some stuff here to handle files
 			     * that are not pcm, or you could just bail out,
 			     */
 			     }                   
                   std::ofstream WAVDATA("WAVDATA.rtf",std::ios::app );
                   WAVDATA << "Id "; 
                   fread(id, sizeof(BYTE), 4, fp);
                   WAVDATA << id;
                   WAVDATA << "\n";
                   fread(&data_size, sizeof(DWORD), 1, fp); 
                   WAVDATA << "Data size: ";
                   WAVDATA << data_size;
                   WAVDATA << "\n";
                   sound_buffer = (BYTE *) malloc(sizeof(BYTE) * data_size);
                   fread(sound_buffer, sizeof(BYTE), data_size, fp); 
                   // Print the array
                   int x=0;
                   for (i = 0; i < data_size;i+=2)
                   { 
                   
                   if (x == 0) (WAVDATA << "DATA WORD ");
                   int	datal = sound_buffer[i];
                   int	datah = sound_buffer[(i+1)];
                   int dataw = datal + datah;
                   WAVDATA << dataw;
                   WAVDATA << " ";
                   x++; 
                   if (x == 2) (WAVDATA << "\n",x=0);
                   }  //	WAVDATA << sound_buffer;
                   WAVDATA << "\n";
                   
                   WAVDATA << "File read successfully."; 
                   WAVDATA.close();
                   }
                   else
                   std::ofstream WAVDATA("WAVDATA.rtf",std::ios::app );
                   WAVDATA << "Error: RIFF file but not a wave file.\n"; 
                   WAVDATA.close();
                   }
                   else
                   std::ofstream WAVDATA("WAVDATA.rtf",std::ios::app );
                   WAVDATA << "Error: not a RIFF file.\n"; 
                   WAVDATA.close();
                   }
                   else
                   {
                   std::ofstream WAVDATA(" WAVDATA.rtf",std::ios::app );
                   WAVDATA << "Can't open file for reading.\n"; 
                   WAVDATA.close();
                   }
                   }            

This block was just a test for one of the buttons.

 void CWaveInFFTDlg::RWAV()
 			     {
 			     // TODO: Add your control notification handler code here 
 			     //	OnBnClickedButwav2() ; 
 			     std::ofstream test("test.rtf",std::ios::out ) 
 			     test << "test button has been pressed.\n"; 
 			     test.close();
 			     }  

This is the output from the code. as I said before the DATA WORD is the output from the left and right channels ,so left = 382 and right = 128

                 Opened C:\Documents and Settings\Danny Mcauliffe\
			        Desktop\FFTMAX\Debug\Jet Plane.wav for reading
 			     Header type RIFF
 			     Data Size 24612
 			     Data Type WAVE
 			     Format header fmt
 			     Format length 16
 			     Format Tag 1
 			     Number of Channels 2
 			     Sample rate 44100
 			     Avg bytes sec 176400
 			     Block align 4
 			     Bits per sample 16
 			     Id data
 			     Data size: 24576
 			     DATA WORD 382 128 

 

Back to main page

Email