/*wav_file_read.cpp
Author: K. L. Srinivas & Hitesh Tulsiani

Problem statement :
Perform frame wise processing of a wave file using a desired analysis window length and desired hop length and ouput the feature along with time stamp.

Variables in code for analysis window and hop length:
window_time_ms -> for setting analysis window length in milisecond.
hop_time_ms    -> for setting hop length in milisecond
These parameters are to be changed as per requirement. Also change the input file name in the code depending on the wav file to process. 

Processing Steps:
1. opens a wav file for reading; 
2. detects the samp. rate and #samples from the header of input wav

Next, we wish to read in sequentially blocks of data from the wav file
for processing one block ("frame") at a time.

In the loop (until last frame):
 3. reads in the data (16-bit integers) in increments of only HOPSIZE
 4. dummy processing()
 5. writes out features calculated at hop intervals along with time stamp in a file.
end loop
*/

// This program is tested on linux machine with g++ compiler as well as windows g++ compiler.

/* To run the code in linux, type following in terminal:
	g++ wav_file_read.cpp
	./a.out
*/

/* To run the code in windows, type following in command prompt:
	g++ wav_file_read.cpp
	a	
*/

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

using namespace std;

// WAVE PCM soundfile format (you can find more in https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ )
typedef struct header_file
{
    char chunk_id[4];
    int chunk_size;
    char format[4];
    char subchunk1_id[4];
    int subchunk1_size;
    short int audio_format;
    short int num_channels;		// number of channels i.e 1-mono 2-stereo
    int sample_rate;			// sample_rate denotes the sampling rate.
    int byte_rate;
    short int block_align;
    short int bits_per_sample;		// number of bits per sample
    char subchunk2_id[4];
    int subchunk2_size;			// subchunk2_size denotes the number of samples.
} header;

typedef struct header_file* header_p;



int main()

{
	/* Input window length and hop length in msec */
	int window_time_ms = 30;		// For window length of 30ms, set window_time_ms to 30
	int hop_time_ms = 10;			// For hop length of 10ms, set hope_time_ms to 10

	/* Enter the name of wav file for processing and output file. File should be present in same folder as this code */
	FILE * infile = fopen("E_Para01.wav","rb");		// Open wave file in read mode
	FILE * outfile = fopen("Output.txt","wb");		// Create output file in write mode

	//cout << "Hop:" << infile << endl;	
	
	int count = 0;						// For counting number of frames in wave file.
	header_p meta = (header_p)malloc(sizeof(header));	// header_p points to a header struct that contains the wave file metadata fields
	int nb;							// variable storing number of byes returned
	if (infile)
	{
		/* Print header information i.e sampling rate, no. of samples, etc */

		fread(meta, 1, sizeof(header), infile);
		//fwrite(meta,1, sizeof(*meta), outfile);
		
		cout << " No. of bits per sample: "<<meta->bits_per_sample << endl;
		cout << " Num of channels: " << meta->num_channels << endl; 
		cout << " Size of Header file is "<<sizeof(*meta)<<" bytes" << endl;
		cout << " Sampling rate of the input wave file is "<< meta->sample_rate <<" Hz" << endl;
		cout << " Number of samples in wave file are " << meta->subchunk2_size << " samples" << endl;
		/* ----------------- Printing header info end -------------------------------------- */
	
		/* Convert time specifications to samples */
		int WINSIZE = ceil((meta->sample_rate)*(window_time_ms)*0.001);	// Round WINSIZE(samples) if WINSIZE is not an integer
		int HOPSIZE = ceil((meta->sample_rate)*(hop_time_ms)*0.001);	// Round HOPSIZE(samples) if HOPSIZE is not an integer
		short int buff16[WINSIZE];			// Defining buffer for holding samples that fall within the window.
		
		short int sample_start,numb_samples;	// Variables for reading samples that fall within window for 1st frame		
		float hop_time = 0;			// Hop time for printing in output file
			
		for (int j = 0;j<WINSIZE ; j++)
			{
				buff16[j] = 0;
			}	
		cout << "Buffer size: "<< WINSIZE << endl;
		cout << "Hop size: " << HOPSIZE << endl;
		//cout << "floor:" << floor((WINSIZE/2)-(HOPSIZE)) << endl;
		
		/* --------------- Converting time specifications to sample end-------------------------------------- */
		
		/* Start reading file frame by frame */
		while (!feof(infile))
		{
			if (count == 0)			
			      {
				sample_start = floor((WINSIZE/2)-(HOPSIZE));
				numb_samples = ceil((WINSIZE/2)+(HOPSIZE));		
				nb = fread(&buff16[sample_start],2,numb_samples,infile);// For 1st frame, read only samples that fall within window and reamining samples are 0.
				
			      }
			else
			      {
				for (int i=0;i<((WINSIZE)-(HOPSIZE));i++)                  // Shifting data in buffer so as to read new data equivalent only to Hop size 
					{
						buff16[i] = buff16[i+(HOPSIZE)]; 
					}
				nb = fread(&buff16[(WINSIZE)-(HOPSIZE)],2,(HOPSIZE),infile); // Reading new data equivalent to HOPSIZE from 2nd frame onwards
					
			      }
			/* ---------- Reading file frame by frame end ---------------------------------- */
 	
		 	/* Insert feature extraction code here. Remove dummy processing block */
			
			/* Dummy processing - Energy calculation */
			float energy = 0;
			
			for (int k=0; k<WINSIZE ; k++)	
				{
					energy = energy + float (buff16[k]*buff16[k]);	
				}
			
			/* -------Dummy processing end ------------------------------- */
			
			/* Print time and feature value to a file */	
			hop_time = hop_time + float(hop_time_ms*0.001);
			fprintf (outfile,"%.03f	%.02f \n",hop_time,energy);	// Ouput file format: Time-Stamp(sec)   Feature Value
										// Example          : 0.01		1234
			
			/* ------printing to file end --------------------------------- */
									
			count++;
		}
	
	cout << " Number of frames in the input wave file are " <<count << endl;  // Print total number of frames
	}
return 0;
}


