1

Hi i have this code for taking as input a wav file and then putting the wah header into a struct and then output it. Everything is good except audioFormat and numChannels but i can't understand why.For example it should output audioFormat: 1 and numChannels: 2 but it outputs audioFormat:0 and numChannels: 1 . I can't understand why this happens.

typedef struct wavHeader
{
    byte chunckID[4];
    dword chunckSize;
    byte format[4];
    byte subchunk1ID[4];
    word subchunk1Size;
    word audioFormat;
    word numChannels;
    dword sampleRate;
    dword byteRate;
    word blockAlign;
    word bitsPerSample;
    byte subchunk2ID[4];
    dword subchunk2Size;
}wav_header;

int check_file_name(char *filename);

void list(char **array) //argv
{
    wav_header wavHeader;
    FILE *pFile;
    if(check_file_name(array[2]) == 0)
    {
        printf("wrong file name\n");
        exit(1);
    }
    pFile = fopen (array[2] ,"r");
    if( pFile != NULL)
    {
        fread(&wavHeader, sizeof(wav_header), 1, pFile);
        fclose(pFile);
        printf("ChunkID: %c%c%c%c\n",wavHeader.chunckID[0],wavHeader.chunckID[1],wavHeader.chunckID[2],wavHeader.chunckID[3]);
        printf("ChunkSize: %d\n",wavHeader.chunckSize);
        printf("Format: %c%c%c%c\n",wavHeader.format[0],wavHeader.format[1],wavHeader.format[2],wavHeader.format[3]);
        printf("SubChunk1ID: %c%c%c%c\n",wavHeader.subchunk1ID[0],wavHeader.subchunk1ID[1],wavHeader.subchunk1ID[2],wavHeader.subchunk1ID[3]);
        printf("Subchunk1Size: %d\n",wavHeader.subchunk1Size);
        printf("AudioFormat: %d\n",wavHeader.audioFormat);
        printf("NumChannels: %d\n",wavHeader.numChannels); 
        printf("SampleRate: %d\n",wavHeader.sampleRate);
        printf("ByteRate: %d\n",wavHeader.byteRate);     
        printf("BlockAlign: %d\n",wavHeader.blockAlign);   
        printf("BitsPerSample: %d\n",wavHeader.bitsPerSample);
        printf("Subchunk2ID: %c%c%c%c\n",wavHeader.subchunk2ID[0],wavHeader.subchunk2ID[1],wavHeader.subchunk2ID[2],wavHeader.subchunk2ID[3]);
        printf("Subchunk2Size: %d\n",wavHeader.subchunk2Size);
    }
    else
    {
        printf("This file doesn't exit\n");
        exit(1);
    }
}
captain monk
  • 719
  • 4
  • 11
  • 34
  • 1
    Have you tried viewing the headers of your `.wav` file with any other tool? Maybe you're expecting the incorrect values, but your program is printing the correct ones. – Paul Jun 26 '13 at 11:27
  • 1
    One word: alignment. (Maybe too: padding.) –  Jun 26 '13 at 11:28
  • Th audio Format annd num Channels as it is in the wav file is 01 00 02 00. so the 0001 is for audio format and 0002 for num channels. So i was expecting it to output audioFormat: 1 and numChannels 2 – captain monk Jun 26 '13 at 11:32

2 Answers2

4

The reason is that your struct wavHeader doesn't really look like you think it does. Let me explain. The C compiler is allowed to change the alignment of the fields in a struct. Typically that means fields are aligned at 4-byte or 8-byte-boundaries. See this discussion about C struct memory layout.

In effect your struct might be laid out something like this in memory:

Byte  1       2      3      4
   +------+------+------+------+
   |          chunckID         |
   +------+------+------+------+
   |         chunckSize        |
   +------+------+------+------+ 
   |           format          |
   +------+------+------+------+
   |        subchunk1ID        |
   +------+------+------+------+
   |subchunk1Size| ~~~~~ ~~~~~   << padding
   +------+------+------+------+   
   | audioFormat | ~~~~~ ~~~~~   << padding
   +------+------+------+------+
   | numChannels | ~~~~~ ~~~~~   << padding
   +------+------+------+------+
   ....

So you see the fields subchunk1Size, audioFormat and numChannels are not contiguous! However, you can use the directive #pragma pack to enforce the layout you intended. Like this:

#pragma pack(push, 1) // exact fit - align at byte-boundary, no padding

typedef struct wavHeader
{
    byte chunckID[4];
    dword chunckSize;
    byte format[4];
    byte subchunk1ID[4];
    word subchunk1Size;
    word audioFormat;
    word numChannels;
    dword sampleRate;
    dword byteRate;
    word blockAlign;
    word bitsPerSample;
    byte subchunk2ID[4];
    dword subchunk2Size;
} wavHeader;

#pragma pack(pop)

Sidenote: When writing this response it occurred to me that you probably also have to be careful about different endianness of multibyte values in the wave header.

Community
  • 1
  • 1
djf
  • 6,592
  • 6
  • 44
  • 62
2

subchunk1Size should be a dword, not a word (see http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html for instance). Your other chunk sizes are declared correctly.

As djf points out you also need to specify the packing and worry about endianness. The WAV header is 16-bit packed, and little-endian. With gcc, I prefer to use #pragma pack(push, 2) before the struct declaration, and #pragma pack(pop) after. And if you #include <endian.h> you can use le32toh and le16toh to read (and their counterparts htole32 and htole16 to write).

Community
  • 1
  • 1
mtrw
  • 34,200
  • 7
  • 63
  • 71
  • Thnx for the replies fellas. I changed the subchunk1Size from word to dword and everything is fine now. What i still don't understand is that i suppose to be carefull about endianess but with just the code i posted everything is fine. Is it the reason because i run it on ubuntu linux 12.04? Is it the same for all linux? – captain monk Jun 27 '13 at 16:58
  • 1
    Endianness actually depends on the CPU, not the OS. So if you're running on x86 or x86_64, your CPU is set up for little-endian already. Actually big-endian is pretty rare these days, some ARMs are set up BE, and some SPARCs. It comes up in embedded programming a bunch, but not usually on desktop/mobile. It does suck when you try and port your code over to a BE processor and have to fix it later, though. More info: http://en.wikipedia.org/wiki/Endianness – mtrw Jun 28 '13 at 00:30