0
votes

I am trying to read an audio wave file from a database of wave files. My code for reading the wave file looks like so

    /**
 * Read and parse a wave file
 *
 **/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "wave.h"
#define TRUE 1 
#define FALSE 0

// WAVE header structure

unsigned char buffer4[4];
unsigned char buffer2[2];

char* seconds_to_time(float seconds);


 FILE *ptr;
 char *filename;
 struct HEADER header;

int main(int argc, char **argv) {

 filename = (char*) malloc(sizeof(char) * 1024);
 if (filename == NULL) {
   printf("Error in malloc\n");
   exit(1);
 }

 // get file path
 char cwd[1024];
 if (getcwd(cwd, sizeof(cwd)) != NULL) {

    strcpy(filename, cwd);

    // get filename from command line
    if (argc < 2) {
      printf("No wave file specified\n");
      return;
    }

    strcat(filename, "/");
    strcat(filename, argv[1]);
    printf("%s\n", filename);
 }

 // open file
 printf("Opening  file..\n");
 ptr = fopen(filename, "rb");
 if (ptr == NULL) {
    printf("Error opening file\n");
    exit(1);
 }

 int read = 0;

 // read header parts

 read = fread(header.riff, sizeof(header.riff), 1, ptr);
 printf("(1-4): %s \n", header.riff); 

 read = fread(buffer4, sizeof(buffer4), 1, ptr);
 printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);

 // convert little endian to big endian 4 byte int
 header.overall_size  = buffer4[0] | 
                        (buffer4[1]<<8) | 
                        (buffer4[2]<<16) | 
                        (buffer4[3]<<24);

 printf("(5-8) Overall size: bytes:%u, Kb:%u \n", header.overall_size, header.overall_size/1024);

 read = fread(header.wave, sizeof(header.wave), 1, ptr);
 printf("(9-12) Wave marker: %s\n", header.wave);

 read = fread(header.fmt_chunk_marker, sizeof(header.fmt_chunk_marker), 1, ptr);
 printf("(13-16) Fmt marker: %s\n", header.fmt_chunk_marker);

 read = fread(buffer4, sizeof(buffer4), 1, ptr);
 printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);

 // convert little endian to big endian 4 byte integer
 header.length_of_fmt = buffer4[0] |
                            (buffer4[1] << 8) |
                            (buffer4[2] << 16) |
                            (buffer4[3] << 24);
 printf("(17-20) Length of Fmt header: %u \n", header.length_of_fmt);

 read = fread(buffer2, sizeof(buffer2), 1, ptr); printf("%u %u \n", buffer2[0], buffer2[1]);

 header.format_type = buffer2[0] | (buffer2[1] << 8);
 char format_name[10] = "";
 if (header.format_type == 1)
   strcpy(format_name,"PCM"); 
 else if (header.format_type == 6)
  strcpy(format_name, "A-law");
 else if (header.format_type == 7)
  strcpy(format_name, "Mu-law");

 printf("(21-22) Format type: %u %s \n", header.format_type, format_name);

 read = fread(buffer2, sizeof(buffer2), 1, ptr);
 printf("%u %u \n", buffer2[0], buffer2[1]);

 header.channels = buffer2[0] | (buffer2[1] << 8);
 printf("(23-24) Channels: %u \n", header.channels);

 read = fread(buffer4, sizeof(buffer4), 1, ptr);
 printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);

 header.sample_rate = buffer4[0] |
                        (buffer4[1] << 8) |
                        (buffer4[2] << 16) |
                        (buffer4[3] << 24);

 printf("(25-28) Sample rate: %u\n", header.sample_rate);

 read = fread(buffer4, sizeof(buffer4), 1, ptr);
 printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);

 header.byterate  = buffer4[0] |
                        (buffer4[1] << 8) |
                        (buffer4[2] << 16) |
                        (buffer4[3] << 24);
 printf("(29-32) Byte Rate: %u , Bit Rate:%u\n", header.byterate, header.byterate*8);

 read = fread(buffer2, sizeof(buffer2), 1, ptr);
 printf("%u %u \n", buffer2[0], buffer2[1]);

 header.block_align = buffer2[0] |
                    (buffer2[1] << 8);
 printf("(33-34) Block Alignment: %u \n", header.block_align);

 read = fread(buffer2, sizeof(buffer2), 1, ptr);
 printf("%u %u \n", buffer2[0], buffer2[1]);

 header.bits_per_sample = buffer2[0] |
                    (buffer2[1] << 8);
 printf("(35-36) Bits per sample: %u \n", header.bits_per_sample);

 read = fread(header.data_chunk_header, sizeof(header.data_chunk_header), 1, ptr);
 printf("(37-40) Data Marker: %s \n", header.data_chunk_header);

 read = fread(buffer4, sizeof(buffer4), 1, ptr);
 printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);

 header.data_size = buffer4[0] |
                (buffer4[1] << 8) |
                (buffer4[2] << 16) | 
                (buffer4[3] << 24 );
 printf("(41-44) Size of data chunk: %u \n", header.data_size);


 // calculate no.of samples
 long num_samples = (8 * header.data_size) / (header.channels * header.bits_per_sample);
 printf("Number of samples:%lu \n", num_samples);

 long size_of_each_sample = (header.channels * header.bits_per_sample) / 8;
 printf("Size of each sample:%ld bytes\n", size_of_each_sample);

 // calculate duration of file
 float duration_in_seconds = (float) header.overall_size / header.byterate;
 printf("Approx.Duration in seconds=%f\n", duration_in_seconds);
 printf("Approx.Duration in h:m:s=%s\n", seconds_to_time(duration_in_seconds));



 // read each sample from data chunk if PCM
 if (header.format_type == 1) { // PCM
    printf("Dump sample data? Y/N?");
    char c = 'n';
    scanf("%c", &c);
    if (c == 'Y' || c == 'y') { 
        long i =0;
        char data_buffer[size_of_each_sample];
        int  size_is_correct = TRUE;

        // make sure that the bytes-per-sample is completely divisible by num.of channels
        long bytes_in_each_channel = (size_of_each_sample / header.channels);
        if ((bytes_in_each_channel  * header.channels) != size_of_each_sample) {
            printf("Error: %ld x %ud <> %ld\n", bytes_in_each_channel, header.channels, size_of_each_sample);
            size_is_correct = FALSE;
        }

        if (size_is_correct) { 
                    // the valid amplitude range for values based on the bits per sample
            long low_limit = 0l;
            long high_limit = 0l;

            switch (header.bits_per_sample) {
                case 8:
                    low_limit = -128;
                    high_limit = 127;
                    break;
                case 16:
                    low_limit = -32768;
                    high_limit = 32767;
                    break;
                case 32:
                    low_limit = -2147483648;
                    high_limit = 2147483647;
                    break;
            }                   

            printf("\n\n.Valid range for data values : %ld to %ld \n", low_limit, high_limit);
            for (i =1; i <= num_samples; i++) {
                printf("==========Sample %ld / %ld=============\n", i, num_samples);
                read = fread(data_buffer, sizeof(data_buffer), 1, ptr);
                if (read == 1) {

                    // dump the data read
                    unsigned int  xchannels = 0;
                    int data_in_channel = 0;

                    for (xchannels = 0; xchannels < header.channels; xchannels ++ ) {
                        printf("Channel#%d : ", (xchannels+1));
                        // convert data from little endian to big endian based on bytes in each channel sample
                        if (bytes_in_each_channel == 4) {
                            data_in_channel =   data_buffer[0] | 
                                                (data_buffer[1]<<8) | 
                                                (data_buffer[2]<<16) | 
                                                (data_buffer[3]<<24);
                        }
                        else if (bytes_in_each_channel == 2) {
                            data_in_channel = data_buffer[0] |
                                                (data_buffer[1] << 8);
                        }
                        else if (bytes_in_each_channel == 1) {
                            data_in_channel = data_buffer[0];
                        }

                        printf("%d ", data_in_channel);

                        // check if value was in range
                        if (data_in_channel < low_limit || data_in_channel > high_limit)
                            printf("**value out of range\n");

                        printf(" | ");
                    }

                    printf("\n");
                }
                else {
                    printf("Error reading file. %d bytes\n", read);
                    break;
                }

            } //    for (i =1; i <= num_samples; i++) {

        } //    if (size_is_correct) { 

     } // if (c == 'Y' || c == 'y') { 
 } //  if (header.format_type == 1) { 

 printf("Closing file..\n");
 fclose(ptr);

  // cleanup before quitting
 free(filename);
 return 0;

}

/**
 * Convert seconds into hh:mm:ss format
 * Params:
 *  seconds - seconds value
 * Returns: hms - formatted string
 **/
 char* seconds_to_time(float raw_seconds) {
  char *hms;
  int hours, hours_residue, minutes, seconds, milliseconds;
  hms = (char*) malloc(100);

  sprintf(hms, "%f", raw_seconds);

  hours = (int) raw_seconds/3600;
  hours_residue = (int) raw_seconds % 3600;
  minutes = hours_residue/60;
  seconds = hours_residue % 60;
  milliseconds = 0;

  // get the decimal part of raw_seconds to get milliseconds
  char *pos;
  pos = strchr(hms, '.');
  int ipos = (int) (pos - hms);
  char decimalpart[15];
  memset(decimalpart, ' ', sizeof(decimalpart));
  strncpy(decimalpart, &hms[ipos+1], 3);
  milliseconds = atoi(decimalpart); 


  sprintf(hms, "%d:%d:%d.%d", hours, minutes, seconds, milliseconds);
  return hms;
}

the wave.h that is included looks like this

// WAVE file header format
struct HEADER {
    unsigned char riff[4];                      // RIFF string
    unsigned int overall_size   ;               // overall size of file in bytes
    unsigned char wave[4];                      // WAVE string
    unsigned char fmt_chunk_marker[4];          // fmt string with trailing null char
    unsigned int length_of_fmt;                 // length of the format data
    unsigned int format_type;                   // format type. 1-PCM, 3- IEEE float, 6 - 8bit A law, 7 - 8bit mu law
    unsigned int channels;                      // no.of channels
    unsigned int sample_rate;                   // sampling rate (blocks per second)
    unsigned int byterate;                      // SampleRate * NumChannels * BitsPerSample/8
    unsigned int block_align;                   // NumChannels * BitsPerSample/8
    unsigned int bits_per_sample;               // bits per sample, 8- 8bits, 16- 16 bits etc
    unsigned char data_chunk_header [4];        // DATA string or FLLR string
    unsigned int data_size;                     // NumSamples * NumChannels * BitsPerSample/8 - size of the next chunk that will be read
};

. I have tried with other wav files and seem to get the correct output (i.e. "RIFF" and "WAVE" from the header). However, with the one I care about this is the output

    ________________________________________________________________________________~/Downloads/libmfcc-master $: ./a.out audioclip-1523814201.wav 
/home/sfelde2/Downloads/libmfcc-master/audioclip-1523814201.wav
Opening  file..
(1-4):  
102 116 121 112
(5-8) Overall size: bytes:1887007846, Kb:1842781 
(9-12) Wave marker: isom
(13-16) Fmt marker: 
105 115 111 109
(17-20) Length of Fmt header: 1836020585 
105 115 
(21-22) Format type: 29545  
111 50 
(23-24) Channels: 12911 
109 112 52 49
(25-28) Sample rate: 825520237
0 0 0 8
(29-32) Byte Rate: 134217728 , Bit Rate:1073741824
102 114 
(33-34) Block Alignment: 29286 
101 101 
(35-36) Bits per sample: 25957 
(37-40) Data Marker:  
109 100 97 116
(41-44) Size of data chunk: 1952539757 
Number of samples:8 
Size of each sample:41891353 bytes
Approx.Duration in seconds=14.059304
Approx.Duration in h:m:s=0:0:14.59
Closing file..

I have started to wonder if the wave field where it says "isom" might signify it is aligned differently but I haven't been able to find anything on ISOM. Thiiis along with the fact that it is not printing the RIFF string and spitting out the wrong values for the data makes me wonder if there is anyway I can figure out where the data starts. Any ideas on what could be the issue or if theres anyway I can debug this so that I can use the wav file would be greatly appreciated.

1
Perhaps the struct needs to be packed? Take a look at sizeof(struct HEADER) and see if it matches what you expect. What compiler are you using (they pretty much all support packed struct, but the syntax differs)? Also maybe the data is stored in big endian? Are you sure the data isn't corrupt? Do media players have any trouble with it? It's really hard to tell what might be wrong unless you post the file in question somewhere… - nemequ
@nemequ I know the struct doesn't need to be packed because I have tried this code on numerous other files and it has worked. I am using gcc version 4.8.5. In my code I make sure to change the endianness when necesarry. I can play the data and it works fine, but maybe I have corrupted the header somehow? - sf8193
Hard to tell without an MCVE. - nemequ
The first 4 bytes aren't even "RIFF". This doesn't look like a wav at all. Actually it looks like an mp4, for which "isom" is a known type tag meaning "ISO media". - user2404501
@nemequ It doesn't need to be packed because all fields in wav header are designed to provide natural alignment (e.g. all int fields are at offsets that are a multiple of 4). The wav header predates the existence of packed structs, so it handled it with careful design. - Craig Estey

1 Answers

0
votes

To anyone facing this issue, I realized the data was getting converted upon download, automatically by my computer. When downloading the files they were getting converted to a .png file for some reason I am not aware of. To circumvent this, if you are getting the same issue I was, make sure to check the type of the file you are inputting, even if it ends in a .wav ending.