Parse the .wav audio file
Now that we have discussed the .wav file format, the initial audio setup, and have our .wav file open in the application, we can process the .wav file based on the structure discussed earlier. First in the .wav file is the RIFF descriptor chunk.
Handle the RIFF descriptor
To handle the initial RIFF header, you can use a typedef structure that is similar to the following:
typedef struct
{
char Riff[4]; /*4 x 1 byte characters = 4 bytes*/
long Size; /*4 byte long*/
char Wave[4]; /*4 x 1 byte characters = 4 bytes*/
}
riff_hdr;
To read the RIFF header, you can write a function similar to the one that appears below. The function determines if we have a .riff file and if it contains .wav data.
int check_hdr(FILE * fp)
The function accepts the parameterFILE *fp, which is an open file pointer to the .wav file. The return value is 0 on a successful run or negative on an unsuccessful run.
int
check_hdr(FILE * fp)
{
riff_hdr riff_header = { "", 0 };
/* Read the header and make sure that this is indeed a Wave file. */
if (fread((unsigned char *) &riff_header, sizeof(riff_hdr), 1, fp)
== 0)
return 0;
if (strncmp(riff_header.Riff, riff_id, strlen(riff_id)) ||
strncmp(riff_header.Wave, wave_id, strlen(wave_id)))
return -1;
return 0;
}
In our calling function, we call check_hdr(). If our function returns a negative value, we have an error case.
if (check_hdr(file) == -1) {
err("check_hdr failed");
goto fail2;
}
If we successfully return from the function, we have a valid .wav file. We can begin parsing the .wav file.
Handle chunk tags
Since each chunk begins with a tag and size, you can create a corresponding structure to handle the tags.
typedef struct
{
char tag[4]; /**4 x 1 byte characters = 4 bytes*/
long length; /*4 byte long*/
}
riff_tag;
To make reading the tags easier, we can abstract the set of statements into a function. After we locate the tag structure, we must return the length of the memory block proceeding the tag. But due to the byte order the fields are stored as, we need to convert the 32-bit (4 byte) length variable to little endian format since we have read the field as an unsigned byte order. To do so, we can use the macro ENDIAN_LE32(), which is declared in gulliver.h.
int find_tag(FILE *fp, const char *tag)
The function accepts the first parameter, FILE *fp, which is an open file pointer to the .wav file. The second parameter, const char *tag, which is a character array with the tag to look for. The value returned is the little endian version of length variable.
int
find_tag(FILE *fp, const char *tag)
{
int ret_val = 0;
riff_tag tag_bfr = { "", 0 };
/* Keep reading until we find the tag or hit the end of file. */
while (fread((unsigned char *) &tag_bfr, sizeof(tag_bfr), 1, fp)) {
/* If this is our tag, set the length and break. */
if (strncmp(tag, tag_bfr.tag, sizeof tag_bfr.tag) == 0) {
ret_val = ENDIAN_LE32(tag_bfr.length);
break;
}
fseek(fp, tag_bfr.length, SEEK_CUR);
}
return (ret_val);
}
Handle the format chunk
To handle the format chunk, we use a typedef structure that is similar to the following:
typedef struct
{
short format_tag;
short channels;
long samples_per_sec;
long avg_bytes_per_sec;
short block_align;
short bits_per_sample;
}
wave_hdr;
Now that we have defined the structure for the format chunk, we should declare an instance of the structure in the global scope.
wave_hdr wav_header;In our calling function, first we find the format tag ("fmt ") using our function find_tag(). Due to the extra parameter space for non-PCM format, we must skip over the length of the format chunk - the size of wave_hdr from the current position of the file pointer.
samples = find_tag(file, "fmt "); fread(&wav_header, sizeof(wav_header), 1, file); fseek(file,(samples - sizeof(wave_hdr)), SEEK_CUR);
Now that we obtained our .wav information, let's display some information to the user. You should also note that you must convert the variables to little endian format.
sample_rate = ENDIAN_LE32(wav_header.samples_per_sec);
sample_channels = ENDIAN_LE16(wav_header.channels);
sample_bits = ENDIAN_LE16(wav_header.bits_per_sample);
snprintf(msg, MSG_SIZE, "SampleRate = %d, channels = %d,
SampleBits = %d\n", sample_rate, sample_channels,
sample_bits);
show_dialog_message(msg);