问题
I have a .wav file and I want to read it in C++. I have done some research on the RIFF file header and wrote a code to load it.
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
using namespace std;
#define BUFFER_LEN 4096
int main(int argc,char * argv[])
{
// Buffers etc..
char ChunkID[4], Format[4], Subchunk1ID[4],Subchunk2ID[4];
int ChunkSize,Subchunk1Size, SampleRate, ByteRate,Subchunk2Size;
short AudioFormat, NumChannels, BlockAlign, BitsPerSample;
// Read the wave file
FILE *fhandle=fopen(argv[1],"rb");
fread(ChunkID,1,4,fhandle);
fread(&ChunkSize,4,1,fhandle);
fread(Format,1,4,fhandle);
fread(Subchunk1ID,1,4,fhandle);
fread(&Subchunk1Size,4,1,fhandle);
fread(&AudioFormat,2,1,fhandle);
fread(&NumChannels,2,1,fhandle);
fread(&SampleRate,4,1,fhandle);
fread(&ByteRate,4,1,fhandle);
fread(&BlockAlign,2,1,fhandle);
fread(&BitsPerSample,2,1,fhandle);
fread(&Subchunk2ID,1,4,fhandle);
fread(&Subchunk2Size,4,1,fhandle);
fclose(fhandle);
// print RIFF info
printf("\%c",ChunkID[0]);
printf("\%c",ChunkID[1]);
printf("\%c",ChunkID[2]);
printf("\%c",ChunkID[3]);
cout << endl;
// print chunk size
printf("%d",ChunkSize);
cout << endl;
// print format
printf("\%c",Format[0]);
printf("\%c",Format[1]);
printf("\%c",Format[2]);
printf("\%c",Format[3]);
cout << endl;
// print sub chunk 1 ID
printf("\%c",Subchunk1ID[0]);
printf("\%c",Subchunk1ID[1]);
printf("\%c",Subchunk1ID[2]);
printf("\%c",Subchunk1ID[3]);
cout << endl;
// print sub chunk 1 size
printf("%d",Subchunk1Size);
cout << endl;
// print audio format
printf("%hd",AudioFormat);
cout << endl;
// print number of channels
printf("%hd",NumChannels);
cout << endl;
return 0;
}
However, it is very wired that I run this code on my input file OS.wav. It output the information:
RIFF
307201488
WAVE
Fake
2
0
28006
You can see the "fmt" is "Fake".
Then I use sox to convert this wav file by:
sox OS.wav test.wav
and run my code again. It has the information:
RIFF
307200050
WAVE
fmt
18
3
2
I did not change any thing. But the header information is so different. Could anyone tell me why this happen, please?
Thank you.
回答1:
You are assuming the fmt
chunk is the first sub-chunk within the RIFF/WAVE
chunk. That is not a requirement or a guarantee. Do some more research on the RIFF/WAV format. WAVE
sub-chunks can appear in any order, the only rule being that the fmt
chunk must appear before the data
chunk, but other chunks can appear before, after, and in between them.
The correct way to parse your file is to loop through the sub-chunks one at a time. Read a chunkID and chunkSize, then read the specified number of bytes (taking padding into account) and process them according to the chunkID as needed, and then repeat until EOF. This lets you process the chunks you are interested in and skip the ones you don't care about. Don't make any assumptions about their order (but do validate the one special case), and definitely don't assume the fmt
chunk is the first chunk, because it might not be.
Try something more like this:
#include <iostream>
#include <istream>
#include <stdexcept>
#include <string>
using namespace std;
struct chunkHdr
{
char id[4];
unsigned int size;
unsigned int pos;
};
bool isChunkID(const chunkHdr &c, char id1, char id2, char id3, char id4)
{
return ((c.id[0] == id1) &&
(c.id[1] == id2) &&
(c.id[2] == id3) &&
(c.id[3] == id4));
}
void read(ifstream &f, void *buffer, streamsize size, chunkHdr *parent)
{
if (!f.read(static_cast<char*>(buffer), size))
{
if (f.eof())
throw runtime_error("Unexpected EOF while reading from file");
throw runtime_error("Unable to read from file");
}
if (parent) parent->pos += size;
}
void skip(ifstream &f, streamsize size, chunkHdr *parent)
{
if (!f.seekg(size, ios_base::cur))
throw runtime_error("Unable to read from file");
if (parent) parent->pos += size;
}
void read(ifstream &f, chunkHdr &c, chunkHdr *parent)
{
read(f, c.id, 4, parent);
read(f, &(c.size), 4, parent);
c.pos = 0;
}
int main(int argc, char * argv[])
{
// Buffers etc..
chunk riff, wave, chk;
bool fmtFound = false;
try
{
// Open the wave file
ifstream wavFile(argv[1], ios_base::binary);
if (!wavFile)
throw runtime_error("Unable to open file");
// check the RIFF header
read(wavFile, riff, NULL);
if (!isChunkID(riff, 'R', 'I', 'F', 'F'))
throw runtime_error("File is not a RIFF file");
cout << "RIFF Size: " << riff.size << endl;
// check the WAVE header
read(wavFile, wave.id, 4, &riff);
wave.size = riff.size - 4;
wave.pos = 0;
cout << "RIFF Type: '" << string(wave.id, 4) << "'" << endl;
if (!isChunkID(wave, 'W', 'A', 'V', 'E'))
throw runtime_error("File is not a WAV file");
// read WAVE chunks
while (wave.pos < wave.size)
{
read(wavFile, chk, &wave);
cout << "Chunk: '" << string(chk.id, 4) << "', Size: " << chk.size << endl;
if (isChunkID(chk, 'f', 'm', 't', ' '))
{
if (fmtFound)
throw runtime_error("More than one FMT chunk encountered");
fmtFound = true;
unsigned int SampleRate, ByteRate;
unsigned short AudioFormat, NumChannels, BlockAlign, BitsPerSample, ExtraSize;
read(wavFile, &AudioFormat, 2, &chk);
read(wavFile, &NumChannels, 2, &chk);
read(wavFile, &SampleRate, 4, &chk);
read(wavFile, &ByteRate, 4, &chk);
read(wavFile, &BlockAlign, 2, &chk);
cout << " Audio Format: " << AudioFormat << endl;
cout << " Channels: " << NumChannels << endl;
cout << " Sample Rate: " << SampleRate << endl;
cout << " Byte Rate: " << ByteRate << endl;
cout << " BlockAlign: " << BlockAlign << endl;
if (chk.size >= 16)
{
read(wavFile, &BitsPerSample, 2, &chk);
cout << " Bits per Sample: " << BitsPerSample << endl;
}
if (chk.size >= 18)
{
read(wavFile, &ExtraSize, 2, &chk);
cout << " Extra Size: " << ExtraSize << endl;
if (ExtraSize > 0)
{
// read and process ExtraSize number of bytes as needed...
skip(wavFile, ExtraSize, &chk);
}
}
if (chk.pos < chk.size)
skip(wavFile, chk.size - chk.pos, &chk);
}
else if (isChunkID(chk, 'd', 'a', 't', 'a'))
{
if (!fmtFound)
throw runtime_error("No FMT chunk encountered before DATA chunk");
// read and process chk.size number of bytes as needed...
skip(wavFile, chk.size, &chk);
}
// read other chunks as needed...
else
{
// skip an unwanted chunk
skip(wavFile, chk.size, &chk);
}
// all done with this chunk
wave.pos += chk.pos;
// check for chunk padding
if (chk.size % 2)
skip(wavFile, 1, &wave);
}
}
catch (const exception &e)
{
cout << "Error! " << e.what() << endl;
}
return 0;
}
来源:https://stackoverflow.com/questions/24986506/error-in-reading-a-wav-file-with-c