Reputation: 2664
I am trying to read the header information from a .wav file, and I'm reading the following properties of the file:
type
size
Format Tag
Format Length
Channels
Sample Rate
Average Bytes Second
Block Align
Bits Per Sample
Data size
Now, everything reads perfectly up until reading in Sample Rate, then, the output is either "0" value or just NULL.
Here is the code: (I have opened the text file, and passed the pointer to the member function)
bool Wav::readHeader(FILE *dataIn)
{
char type2[4];
fread(this->type, sizeof(char), 4, dataIn);
fread(&this->size, sizeof(int), 1, dataIn);
fread(this->type, sizeof(char), 4, dataIn);
fread(this->type, sizeof(char), 4, dataIn);
fread(&this->format_length, sizeof(short), 1, dataIn);
fread(&this->format_tag, sizeof(short), 1, dataIn);
fread(&this->channels, sizeof(short), 1, dataIn);
fread(&this->sample_rate, sizeof(unsigned int), 1, dataIn);
fread(&this->avg_bytes_sec, sizeof(unsigned short), 1, dataIn);
fread(&this->block_align, sizeof(short), 1, dataIn);
fread(&this->bits_per_sample, sizeof(short), 1, dataIn);
return true;
}
And the class members are defined as:
char type[4];
int size;
unsigned short format_tag;
unsigned short format_length;
unsigned short channels;
unsigned int sample_rate;
unsigned short avg_bytes_sec;
unsigned short block_align;
unsigned short bits_per_sample;
unsigned int data_size;
Does anyone have any solutions or suggestions?
(P.S. Apologies if this question is not right. I have tried to write it the best I could!)
Upvotes: 0
Views: 3190
Reputation: 38217
If you had tightly packed structs representing the (PCM) WAVE's header, it would look like this:
struct Riff
{
char chunkId[4]; // "RIFF" (assuming char is 8 bits)
int chunkSize; // (assuming int is 32 bits)
char format[4]; // "WAVE"
};
struct Format
{
char chunkId[4]; // "fmt "
int chunkSize;
short format; // assuming short is 16 bits
short numChannels;
int sampleRate;
int byteRate;
short align;
short bitsPerSample;
};
struct Data
{
char chunkId[4]; // "data"
int chunkSize; // length of data
char* data;
};
struct Wave // Actual structure of a PCM WAVE file
{
Riff riffHeader;
Format formatHeader;
Data dataHeader;
};
Essentially, your problem is that format_length
(Format::chunkSize
in my struct) and avg_bytes_sec
(Format::byteRate
in my struct) are 2 bytes each in your code, but they should each be 4. You're also not reading the data subchunk's header, but I assume that's intentional?
To read, you can do something like this:
void readWave(std::ifstream& file, Wave& wave)
{
// First read the RIFF header
file.read(wave.riffHeader.chunkId, 4);
file.read(reinterpret_cast<char*>(&wave.riffHeader.chunkSize), 4);
file.read(wave.riffHeader.format, 4);
// Now read the FORMAT header
file.read(wave.formatHeader.chunkId, 4);
file.read(reinterpret_cast<char*>(&wave.formatHeader.chunkSize), 4);
file.read(reinterpret_cast<char*>(&wave.formatHeader.format), 2);
file.read(reinterpret_cast<char*>(&wave.formatHeader.numChannels), 2);
file.read(reinterpret_cast<char*>(&wave.formatHeader.sampleRate), 4);
file.read(reinterpret_cast<char*>(&wave.formatHeader.byteRate), 4);
file.read(reinterpret_cast<char*>(&wave.formatHeader.align), 2);
file.read(reinterpret_cast<char*>(&wave.formatHeader.bitsPerSample), 2);
// Now read the DATA header
file.read(wave.dataHeader.chunkId, 4);
file.read(reinterpret_cast<char*>(&wave.dataHeader.chunkSize), 4);
// The actual data goes in wave.dataHeader.data, so you can allocate it
// and then read direclty into it now
}
Michael's link is the one I always use when I need to read/write a WAVE file (which I've had to do several times). I suggest you read it carefully.
If you've got extra chunks in your WAVE file before your "data" chunk, this code should be able to skip them:
void readWave(std::ifstream& file, Wave& wave)
{
// First read the RIFF header
file.read(wave.riffHeader.chunkId, 4);
file.read(reinterpret_cast<char*>(&wave.riffHeader.chunkSize), 4);
file.read(wave.riffHeader.format, 4);
// Now read the FORMAT header
file.read(wave.formatHeader.chunkId, 4);
file.read(reinterpret_cast<char*>(&wave.formatHeader.chunkSize), 4);
file.read(reinterpret_cast<char*>(&wave.formatHeader.format), 2);
file.read(reinterpret_cast<char*>(&wave.formatHeader.numChannels), 2);
file.read(reinterpret_cast<char*>(&wave.formatHeader.sampleRate), 4);
file.read(reinterpret_cast<char*>(&wave.formatHeader.byteRate), 4);
file.read(reinterpret_cast<char*>(&wave.formatHeader.align), 2);
file.read(reinterpret_cast<char*>(&wave.formatHeader.bitsPerSample), 2);
// WAVE files are just a special type of RIFF file, so it's possible
// there are other chunks, like "fact" chunks. We'll skip over these
// extra chunks until we find a "data" chunk
char chunkId[4] = {0};
int chunkSize = 0;
while (file.read(chunkId, 4) &&
(chunkId[0] != 'd' ||
chunkId[1] != 'a' ||
chunkId[2] != 't' ||
chunkId[3] != 'a'))
{
file.read(reinterpret_cast<char*>(&chunkSize), 4); // Read the chunk's size
file.seekg(chunkSize, std::ios_base::cur); // Skip the chunk
}
// We've found the DATA chunk and header
wave.dataHeader.chunkId[0] = chunkId[0];
wave.dataHeader.chunkId[1] = chunkId[1];
wave.dataHeader.chunkId[2] = chunkId[2];
wave.dataHeader.chunkId[3] = chunkId[3];
file.read(reinterpret_cast<char*>(&wave.dataHeader.chunkSize), 4);
// The actual data goes in wave.dataHeader.data, so you can allocate it
// and then read direclty into it now
}
Upvotes: 2