2010/05/29

How to read WAV format file in C++


Recently I'm doing a project about audio. The first problem I met is getting the WAV into memory. If this is not done, even the best theory cannot come true. So let's come to this problem.

Objectives:
Read a WAV file into C++ code.

Prerequisite:
Understanding pointer and memory organization in C++.

Why WAV? Actually, I want to talk about getting any type of files into my codes. Because any type of files is stored the same way. When you open a file with a binary viewer, you see something like "24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d" in hexadecimal notation. What I want to say is, we don't have to differ one type from another type and just read these bytes one by one into memory and that's all.

To work with files, we include fstream first. A file is like another area of memory. We can employ a pointer to manipulate it.


FILE* fp = fopen(fname,"rb");

fname is a string in the form of "filepath/filename.extension", and "rb" means "read only". If the file doesn't exist, fp is NULL.

Then we use fread function to read contents into memory and move the pointer in the meantime. For example, we we want to read 4 short type values into memory.


short arr_short[4];
fread(arr_short, sizeof(short), 4, fp);

Also, we can read 1 long type value into memory.


long num_long;
fread(&num_long, sizeof(long), 1, fp);

Please note that the first argument is an address. Another thing, the pointer moves automatically. When we first read 4 short type values, the pointer move forward a distance of 4 shorts.

After we read all that we want, we should turn off the pointer to make the process more stable.


fclose(fp);

I guess you can handle a file now. Then we go on to WAV file. Different files have different organizations. This is the organization of WAV file. (I copy this from Stanford)

We see that in the beginning of the file the bytes have specific meanings. Now I don't want to talk about it anymore. See the codes below.

The beginning part defines a class to be the entrance of the WAV data read from a file.

#include<fstream>
#include<iostream>
using namespace std;

class WavData {
    public:
        short* data;
        unsigned long size;
        
        WavData() {
            data = NULL;
            size = 0;
        }
};


Then comes the function processing the reading job.


void loadWaveFile(char *fname, WavData *ret) {
    
    FILE* fp = fopen(fname,"rb");
    if (fp) {
        char id[5];
        unsigned long size;
        short format_tag, channels, block_align, bits_per_sample;
        unsigned long format_length, sample_rate, avg_bytes_sec, data_size;
        
        fread(id, sizeof(char), 4, fp);
        id[4] = '\0';
        
        if (!strcmp(id, "RIFF")) {
            fread(&size, sizeof(unsigned long), 1, fp);
            fread(id, sizeof(char), 4, fp);
            id[4] = '\0';
            
            if (!strcmp(id,"WAVE")) {
                fread(id, sizeof(char), 4, fp);
                fread(&format_length, sizeof(unsigned long),1,fp);
                fread(&format_tag, sizeof(short), 1, fp);
                fread(&channels, sizeof(short),1,fp);
                fread(&sample_rate, sizeof(unsigned long), 1, fp);
                fread(&avg_bytes_sec, sizeof(unsigned long), 1, fp);
                fread(&block_align, sizeof(short), 1, fp);
                fread(&bits_per_sample, sizeof(short), 1, fp);
                fread(id, sizeof(char), 4, fp);
                fread(&data_size, sizeof(unsigned long), 1, fp);
                
                ret->size = data_size/sizeof(short);
                ret->data = (short*) malloc(data_size);
                fread(ret->data, sizeof(short), ret->size, fp);
                }
            else {
                cout<<"Error: RIFF file but not a wave file\n";
                }
            }
        else {
            cout<<"Error: not a RIFF file\n";
            }
        }
    fclose(fp);
    }


Finally the main function does the testing.


int main() {
    WavData song;
    loadWaveFile("audio.wav",&song);    
    cout<<"there are "<<song.size/2<<" samples in this WAV file."<<endl;
    return 0;
    }

I know you have one more question: What if I don't need all these segments and just want the data? It's easy. Please refer to "fseek" and you will know how it can be done.
See you next time.

2010/05/28

The simpliest coding of all time

Hello buddy! Welcome to my digital world! What a nice pleasure to have you here. Technically speaking, I am not an expert in programming or any other digital technologies. I am someone who just enjoys the process of using instead of digging deeper. If you want to apply your ideas into practice as quick as possible, it's the place you are looking for.

So I would like to explain anything in the easiest way. In every blog, I will list specific objectives, prerequisites and the article is all about achieve these objectives.

Most blogs are about shortcuts that I accumulate during the process of daily research. Many ideas come from other experts' technical blogs but I modify them into a much simpler way to share.

Here is an example. As simple as possible.

------------------------------------------

Objectives:
To implement the easiest programs in Java and C++.

Prerequisite:
As long as you have install JDK and Dev-CPP in your computer.

OK. Let's go. I assume you know nothing about programming. Just copy these codes into any code editor (such as UltraEdit). Compile and run.

Java Implementation

public class HelloWorld{
 public static void main(String[] args){
   System.out.println("helloWorld");
 }
}

C++ implementation

#include<iostream>
using namespace std;

int main() {
    cout<<"Welcome to my digital world!"<<endl;
    return 0;    
}



In each codes, only one line makes a little sense, right? For other lines we don't have to change regularly. If you see "Welcome to my digital world!" on the screen, class is over.