|
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 |