//=========================================================================
#define PROGRAMMER "SOLUTIONS, Nofrills"
#define PROG_CODE  "soln"
#define COURSE     "ECE-1021"
#define YEAR       (2004)
#define TERM       "Spring"
#define SECTION    (0)
#define ASSIGNMENT "HW #8B"
#define REVISION   (0)
#define TITLE      "WAV <-> TXT Converter"
#define SUBTITLE   "Last Modified on 22 APR 04"
#define EMAIL      "ece1021@eas.uccs.edu"
#define FILENAME   "08b0soln.txt"
//=========================================================================

//=========================================================================
// PROBLEM
//=========================================================================
//
// Get a file name from the User that has either a TXT or a WAV extension.
// If (TXT)
//    Read the text file and generate a wave file.
// If (WAV)
//    Read the wave file and generate a text file.
//
//=========================================================================
// PSEUDOCODE
//=========================================================================
//
// 1) TASK: Get filename from User
// 2) TASK: Determine type of file (TXT or WAV or neither)
// 3) TASK: Open and verify files in appropriate modes
// 4) IF: Both files successfully opened
// 4.1) IF: (Input file is TXT)
// 4.1.1) TASK: Read header
// 4.1.2) TASK: Determine amount of data in file
// 4.1.3) TASK: Write WAV file (except for actual data)
// 4.1.4) TASK: Translate data from TXT to WAV
// 4.2) IF: (Input file is WAV)
// 4.2.1) TASK: Read Format Chunk
// 4.2.2) TASK: Write TXT header
// 4.2.3) TASK: Translate data from WAV to TXT

//=========================================================================
// DEVIATIONS FROM SUBMITTED PSEUDOCODE
//=========================================================================
//
// In this section you should list any differences between the pseudocode
// you submitted for grading and the program as you implemented it.
//
// If you did not submit any pseudocode, then you must put your entire
// pseudocode here or lose even more points.

//=========================================================================
// WAIVED COMPILER WARNINGS
//=========================================================================
//
// In this section you should list any compiler warnings that remain and
// why you are accepting them. It is acceptable to indicate that the
// the reason is that you don't know how to get rid of it.
//
// Linker Warning: No module definition file specified: using defaults
// Reason for Waiver: Can't suppress. Does not have adverse impact.

//=========================================================================
// CODE SECTION
//=========================================================================

//== INCLUDE FILES ========================================================
#include <stdio.h> // printf(), scanf(), fprintf(), fscanf()
						 // fopen(), fclose(), fseek(), fread(), fwrite()
#include <math.h>  // pow()
#include <string.h> // strcmp(), strlen(), strlwr()

//== MACRO DEFINITIONS ====================================================
#define FALSE (0)
#define TRUE (!FALSE)
#define BLANKLINE printf("\n")

#define TXT     (0)
#define WAV     (1)
#define NEITHER (2)

//=========================================================================
//== WAVEFILE STRUCTURE DEFINITION ========================================
//=========================================================================

struct wavfile
{
	long RIFF_FileSize;
	long FMT_Offset;    // Offset from beginning of file to FORMAT chunk
	long FMT_ChunkSize;
	int  FMT_AudioFormat;
	int  FMT_NumChannels;
	long FMT_SampleRate;
	long FMT_ByteRate;
	int  FMT_BlockAlign;
	int  FMT_BitsPerSample;
	long DATA_Offset;   // Offset from beginning of file to DATA chunk
	long DATA_ChunkSize;
};
typedef struct wavfile WAVFILE;

// --- OFFSETS within WAV File  -------------------------------------------

// Chunk Offsets common to all chunk types
#define CHUNK_ID      ( 0)
#define CHUNK_SIZE    ( 4)
#define FIRSTSUBCHUNK (12)
// RIFF Chunk Offsets (From beginning of RIFF Chunk)
#define RIFFTYPE      ( 8)
// FMT Chunk Offsets (from beginning of FORMAT chunk)
#define AUDIOFORMAT   ( 8)
#define NUMCHANNELS   (10)
#define SAMPLERATE    (12)
#define BYTERATE      (16)
#define BLOCKALIGN    (20)
#define BITSPERSAMPLE (22)
// DATA Chunk Offsets (from beginning of DATA chunk)
#define DATASTART     ( 8)

// --- WAVFILE Structure Primitive Function Prototypes --------------------

long GetWAVFILE_RIFF_Filesize(WAVFILE *wav);
long SetWAVFILE_RIFF_Filesize(WAVFILE *wav, long v);
long GetWAVFILE_FMT_Offset(WAVFILE *wav);
long SetWAVFILE_FMT_Offset(WAVFILE *wav, long v);
long GetWAVFILE_FMT_ChunkSize(WAVFILE *wav);
long SetWAVFILE_FMT_ChunkSize(WAVFILE *wav, long v);
int  GetWAVFILE_FMT_AudioFormat(WAVFILE *wav);
int  SetWAVFILE_FMT_AudioFormat(WAVFILE *wav, int v);
int  GetWAVFILE_FMT_NumChannels(WAVFILE *wav);
int  SetWAVFILE_FMT_NumChannels(WAVFILE *wav, int v);
long GetWAVFILE_FMT_SampleRate(WAVFILE *wav);
long SetWAVFILE_FMT_SampleRate(WAVFILE *wav, long v);
long GetWAVFILE_FMT_ByteRate(WAVFILE *wav);
long SetWAVFILE_FMT_ByteRate(WAVFILE *wav, long v);
int  GetWAVFILE_FMT_BlockAlign(WAVFILE *wav);
int  SetWAVFILE_FMT_BlockAlign(WAVFILE *wav, int v);
int  GetWAVFILE_FMT_BitsPerSample(WAVFILE *wav);
int  SetWAVFILE_FMT_BitsPerSample(WAVFILE *wav, int v);
long GetWAVFILE_DATA_Offset(WAVFILE *wav);
long SetWAVFILE_DATA_Offset(WAVFILE *wav, long v);
long GetWAVFILE_DATA_ChunkSize(WAVFILE *wav);
long SetWAVFILE_DATA_ChunkSize(WAVFILE *wav, long v);

//=========================================================================


//== FUNCTION PROTOTYPES (for Primary Functions) ==========================
void PrintHeader(void);
void GetFileNameFromUser(char *filename, int max);
int  TypeOfFile(char *filename);
FILE *fopenTXT(char *filename, int InputType);
FILE *fopenWAV(char *filename, int InputType);
long CountSampleFrames(FILE *fpTXT, double *min, double *max);
void WriteWAVparams(FILE *fpWAV, int channels, int bits,
											long rate, long frames);
int WriteWAV2TXTdata(WAVFILE *wav, FILE *fpTXT, FILE *fpWAV);
int WriteTXT2WAVdata(WAVFILE *wav, FILE *fpWAV, FILE *fpTXT,
											  double min, double max);
int  GetWAVchannels(FILE *fpWAV);
int  GetWAVbits(FILE *fpWAV);
long GetWAVrate(FILE *fpWAV);

WAVFILE *WriteWAVEFILE_Header(WAVFILE *wav, FILE *fp);
WAVFILE *ReadWAVEFILE_Header(WAVFILE *wav, FILE *fp);
int BytesPerSample(int bits);
int PrepWAVFILE(WAVFILE *wav, int chan, int bits, long rate, long frames);
WAVFILE *DisplayWAVEFILE(WAVFILE *wav);

//== MAIN FUNCTION ========================================================
int main(void)
{
	char filename[81];
	int InputType;
	FILE *fpTXT, *fpWAV;
	int chan, bits;
	long rate, frames;
	WAVFILE WaveFile, *wav;
	double min, max;

	PrintHeader();
	BLANKLINE;

	wav = &WaveFile; // For convenience in function calls

	// 1) TASK: Get filename from User
	GetFileNameFromUser(filename, 81);

	// 2) TASK: Determine type of file (TXT or WAV or neither)
	InputType = TypeOfFile(filename);

	// 3) TASK: Open and verify files in appropriate modes
	fpTXT = fopenTXT(filename, InputType);
	fpWAV = fopenWAV(filename, InputType);

	// 4) IF: Both files successfully opened
	if( (NULL != fpTXT) && (NULL != fpWAV) )
	{
		// 4.1) IF: (Input file is TXT)
		if(TXT == InputType)
		{
			// 4.1.1) TASK: Read header
			fscanf(fpTXT, "%i,",  &chan);
			fscanf(fpTXT, "%i,",  &bits);
			fscanf(fpTXT, "%li,", &rate);
			// 4.1.2) TASK: Determine amount of data in file
			frames = CountSampleFrames(fpTXT, &min, &max);
			// 4.1.3) TASK: Write WAV file (except for actual data)
			PrepWAVFILE(wav, chan, bits, rate, frames);
			DisplayWAVEFILE(wav);
			WriteWAVEFILE_Header(wav, fpWAV);
			// 4.1.4) TASK: Translate data from TXT to WAV
			WriteTXT2WAVdata(wav, fpTXT, fpWAV, min, max);
			printf("Text File Generated. Program Finished.\n");
		}
		// 4.2) IF: (Input file is WAV)
		if(WAV == InputType)
		{
			// 4.2.1) TASK: Read Format Chunk
			if(NULL == ReadWAVEFILE_Header(wav, fpWAV))
			{
				printf("Failed to read WAV Header.\n");
				return(2);
			}
			DisplayWAVEFILE(wav);

			// 4.2.2) TASK: Write TXT header
			chan = GetWAVFILE_FMT_NumChannels(wav);
			bits = GetWAVFILE_FMT_BitsPerSample(wav);
			rate = GetWAVFILE_FMT_SampleRate(wav);
			fprintf(fpTXT, "%i,%i,%li\n", chan, bits, rate);

			// 4.1.4) TASK: Translate data from TXT to WAV
			WriteWAV2TXTdata(wav, fpWAV, fpTXT);
			printf("Wave File Generated. Program Finished.\n");
		}

	fclose(fpTXT);
	fclose(fpWAV);
	}

	return(0);
}

//=========================================================================
// PRIMARY FUNCTIONS (functions called directly by main() )
//=========================================================================

//== FUNCTION PROTOTYPES (for Support Functions) ==========================
int printc(char c, int n);
int fwritelong(FILE *fp, long offset, long value);
int fwriteint(FILE *fp, long offset, int value);
int fwritebytes(FILE *fp, long offset, char *value, int n);

//== PRIMARY FUNCTIONS ====================================================
void PrintHeader(void)
{
	printc('=', 79); printf("\n");
	printf("Course....... %s-%i (%s %i)\n", COURSE, SECTION, TERM, YEAR);
	printf("Programmer... %s (%s)\n", PROGRAMMER, PROG_CODE);
	printf("Assignment... %s (Rev %i) (Source Code in %s)\n",
		ASSIGNMENT, REVISION, FILENAME);
	printf("Description.. %s\n", TITLE);
	printf("              %s\n", SUBTITLE);
	printc('=', 79); printf("\n");

	return;
}

//=========================================================================
// SUPPORT FUNCTIONS (functions not called directly by main() )
//=========================================================================

int printc(char c, int n)
{
	while(0 < n--)
		printf("%c", c);

	return(n);
}

char *StripCR(char *s)
{
	int i;

	i = 0;
	do
	{
		switch(s[i])
		{
			case '\0':
				break;
			case '\n':
			case '\r':
				s[i] = '\0';
				break;
			default :
				i++;
		}
	} while(s[i] != '\0');

	return(s);
}

void GetFileNameFromUser(char *filename, int max)
{
	printf("Enter filename with extension: ");
	fgets(filename, max, stdin);
	StripCR(filename);
	return;
}

int TypeOfFile(char *s)
{
	char *ext;
	int i;
	int type = NEITHER;

	ext = s;
	i = 0;
	do
	{
		switch(s[i])
		{
			case '\0':
				break;
			case '.':
				ext = s+i+1;
				s[i] = '\0';
				break;
			default :
				i++;
		}
	} while(s[i] != '\0');

	if(ext > s) // A '.' was found
	{
		strlwr(ext);
		if(0 == strcmp(ext, "txt"))
			type = TXT;
		if(0 == strcmp(ext, "wav"))
			type = WAV;
	}

	return(type);
}

FILE *fopenTXT(char *filename, int InputType)
{
	char fullname[256];
	FILE *fp;

	strcpy(fullname, filename);
	strcat(fullname, ".txt");

	switch(InputType)
	{
		case TXT:
			fp = fopen(fullname, "rt");
			break;
		case WAV:
			fp = fopen(fullname, "wt");
			break;
		default :
			fp = NULL;
			break;
	}

	return(fp);
}

FILE *fopenWAV(char *filename, int InputType)
{
	char fullname[256];
	FILE *fp;

	strcpy(fullname, filename);
	strcat(fullname, ".wav");

	switch(InputType)
	{
		case TXT:
			fp = fopen(fullname, "wb");
			break;
		case WAV:
			fp = fopen(fullname, "rb");
			break;
		default :
			fp = NULL;
			break;
	}

	return(fp);
}

long CountSampleFrames(FILE *fpTXT, double *min, double *max)
{
	long frames;
	char buffer[256];
	double value;

	// Pass 1: Determine min and max:

	rewind(fpTXT);
	fgets(buffer, 256, fpTXT);  // Skip first line;

	fscanf(fpTXT, "%lf", &value);
	*min = *max = value;

	while(1 == fscanf(fpTXT, "%lf", &value) )
	{
		if(value < *min)
			*min = value;
		if(value > *max)
			*max = value;
	}

	// Pass 2: Count sample frames:

	rewind(fpTXT);
	fgets(buffer, 256, fpTXT);  // Skip first line;

	frames = 0;
	while( NULL != fgets(buffer, 256, fpTXT) )
		frames++;

	return(frames);
}

int fwritelong(FILE *fp, long offset, long value)
{
	if( fseek(fp, offset, SEEK_SET) ) // returns zero upon success
		return(1);

	if(1 != fwrite(&value, 4, 1, fp) )    // returns number of items written
		return(2);

	return(0);
}

int fwriteint(FILE *fp, long offset, int value)
{
	if( fseek(fp, offset, SEEK_SET) ) // returns zero upon success
		return(1);

	if(1 != fwrite(&value, 2, 1, fp) )    // returns number of items written
		return(2);

	return(0);
}

int fwritebytes(FILE *fp, long offset, char *value, int n)
{
	if( fseek(fp, offset, SEEK_SET) ) // returns zero upon success
		return(1);

	if(n != fwrite(value, 1, n, fp) )    // returns number of items written
		return(2);

	return(0);
}

long freadlong(FILE *fp, long offset)
{
	long value;

	if( fseek(fp, offset, SEEK_SET) ) // returns zero upon success
		return(-1);

	if(1 != fread(&value, 4, 1, fp) )    // returns number of items read
		return(-1);

	return(value);
}

int freadint(FILE *fp, long offset)
{
	int value;

	if( fseek(fp, offset, SEEK_SET) ) // returns zero upon success
		return(-1);

	if(1 != fread(&value, 2, 1, fp) )    // returns number of items read
		return(-1);

	return(value);
}

int freadbytes(FILE *fp, long offset, char *value, int n)
{
	if( fseek(fp, offset, SEEK_SET) ) // returns zero upon success
		return(1);

	if(n != fread(value, 1, n, fp) )    // returns number of items read
		return(2);

	return(0);
}

//=========================================================================
// WAVFILE STRUCTURE FUNCTIONS
//=========================================================================

// --- WAV File Organization ----------------------------------------------

// RIFF Chunk
// 4 GroupID       'RIFF'
// 4 FileSize      Bytes Remaining in File
// 4 RIFFtype      'WAVE'

// FORMAT Chunk
// 4 ChunkID       'fmt '
// 4 ChunkSize     Bytes Remaining in Chunk
// 2 AudioFormat   PCM=1 for uncompressed
// 2 NumChannels
// 4 SampleRate    Sample Frames per Second
// 4 ByteRate      Bytes per Second
// 2 BlockAlign    Bytes per Frame
// 2 BitsPerSample

// DATA Chunk
// 4 ChunkID     'data'
// 4 ChunkSize   Bytes Remaining in Chunk
// x Data

// --- WAVFILE Structure Primitive Functions ------------------------------

// RIFF_FileSize
long GetWAVFILE_RIFF_Filesize(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->RIFF_FileSize);
}

long SetWAVFILE_RIFF_Filesize(WAVFILE *wav, long v)
{
	if(NULL == wav)
		return(-1);
	wav->RIFF_FileSize = v;
	return(GetWAVFILE_RIFF_Filesize(wav));
}

// FMT_Offset
long GetWAVFILE_FMT_Offset(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->FMT_Offset);
}

long SetWAVFILE_FMT_Offset(WAVFILE *wav, long v)
{
	if(NULL == wav)
		return(-1);
	wav->FMT_Offset = v;
	return(GetWAVFILE_FMT_Offset(wav));
}

// FMT_ChunkSize
long GetWAVFILE_FMT_ChunkSize(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->FMT_ChunkSize);
}

long SetWAVFILE_FMT_ChunkSize(WAVFILE *wav, long v)
{
	if(NULL == wav)
		return(-1);
	wav->FMT_ChunkSize = v;
	return(GetWAVFILE_FMT_ChunkSize(wav));
}

// FMT_AudioFormat
int GetWAVFILE_FMT_AudioFormat(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->FMT_AudioFormat);
}

int SetWAVFILE_FMT_AudioFormat(WAVFILE *wav, int v)
{
	if(NULL == wav)
		return(-1);
	wav->FMT_AudioFormat = v;
	return(GetWAVFILE_FMT_AudioFormat(wav));
}

// FMT_NumChannels
int GetWAVFILE_FMT_NumChannels(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->FMT_NumChannels);
}

int SetWAVFILE_FMT_NumChannels(WAVFILE *wav, int v)
{
	if(NULL == wav)
		return(-1);
	wav->FMT_NumChannels = v;
	return(GetWAVFILE_FMT_NumChannels(wav));
}

//FMT_SampleRate
long GetWAVFILE_FMT_SampleRate(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->FMT_SampleRate);
}

long SetWAVFILE_FMT_SampleRate(WAVFILE *wav, long v)
{
	if(NULL == wav)
		return(-1);
	wav->FMT_SampleRate = v;
	return(GetWAVFILE_FMT_SampleRate(wav));
}

// FMT_ByteRate
long GetWAVFILE_FMT_ByteRate(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->FMT_ByteRate);
}

long SetWAVFILE_FMT_ByteRate(WAVFILE *wav, long v)
{
	if(NULL == wav)
		return(-1);
	wav->FMT_ByteRate = v;
	return(GetWAVFILE_FMT_ByteRate(wav));
}

// FMT_BlockAlign
int GetWAVFILE_FMT_BlockAlign(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->FMT_BlockAlign);
}

int SetWAVFILE_FMT_BlockAlign(WAVFILE *wav, int v)
{
	if(NULL == wav)
		return(-1);
	wav->FMT_BlockAlign = v;
	return(GetWAVFILE_FMT_BlockAlign(wav));
}

// FMT_BitsPerSample
int GetWAVFILE_FMT_BitsPerSample(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->FMT_BitsPerSample);
}

int SetWAVFILE_FMT_BitsPerSample(WAVFILE *wav, int v)
{
	if(NULL == wav)
		return(-1);
	wav->FMT_BitsPerSample = v;
	return(GetWAVFILE_FMT_BitsPerSample(wav));
}

// DATA_Offset
long GetWAVFILE_DATA_Offset(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->DATA_Offset);
}

long SetWAVFILE_DATA_Offset(WAVFILE *wav, long v)
{
	if(NULL == wav)
		return(-1);
	wav->DATA_Offset = v;
	return(GetWAVFILE_DATA_Offset(wav));
}

// DATA_ChunkSize
long GetWAVFILE_DATA_ChunkSize(WAVFILE *wav)
{
	if(NULL == wav)
		return(-1);

	return(wav->DATA_ChunkSize);
}

long SetWAVFILE_DATA_ChunkSize(WAVFILE *wav, long v)
{
	if(NULL == wav)
		return(-1);
	wav->DATA_ChunkSize = v;
	return(GetWAVFILE_DATA_ChunkSize(wav));
}

int IsRIFFWAVE(FILE *fp)
{
	char IDstring[5];

	if(NULL == fp)
		return(FALSE);

	IDstring[4] = '\0';

	freadbytes(fp, 0, IDstring, 4);
	if(0 != strcmp(IDstring, "RIFF"))
		return(FALSE);

	freadbytes(fp, RIFFTYPE, IDstring, 4);
	if(0 != strcmp(IDstring, "WAVE"))
		return(FALSE);

	return(TRUE);
}

long FindChunk(FILE *fp, char *ChunkType)
{
	long offset;
	long filesize;
	int  NotFound;
	char IDstring[5];

	// Verify the RIFF/WAVE Header
	if(!IsRIFFWAVE(fp))
		return(0);

	filesize = 8 + freadlong(fp, CHUNK_SIZE);
	// If ChunkType is 'RIFF', function returns filesize.
	if(0 == strcmp(ChunkType, "RIFF"))
		return(filesize);

	// Loop Initialization
	IDstring[4] = '\0';
	offset = FIRSTSUBCHUNK;
	NotFound = TRUE;

	// Loop until Chunk is found or no more chunks in file
	do
	{
		freadbytes(fp, offset, IDstring, 4);
		if(0 == strcmp(ChunkType, IDstring))
			NotFound = FALSE;
		else
			offset += 8 + freadlong(fp, offset + CHUNK_SIZE);
	} while( (offset < filesize) && NotFound);

	return(NotFound? 0 : offset);
}

WAVFILE *WriteWAVEFILE_Header(WAVFILE *wav, FILE *fp)
{
	long cs; // Chunk Start

	if( (NULL == fp) || (NULL == wav) )
		return(NULL);

	// Write RIFF Header
	cs = 0;
	fwritebytes(fp, cs + CHUNK_ID, "RIFF", 4);
	fwritelong (fp, cs + CHUNK_SIZE, GetWAVFILE_RIFF_Filesize(wav));
	fwritebytes(fp, cs + RIFFTYPE, "WAVE", 4);

	// Write FORMAT Chunk
	cs = GetWAVFILE_FMT_Offset(wav);
	fwritebytes(fp, cs + CHUNK_ID, "fmt ", 4);
	fwritelong (fp, cs + CHUNK_SIZE, GetWAVFILE_FMT_ChunkSize(wav));
	fwriteint  (fp, cs + AUDIOFORMAT, GetWAVFILE_FMT_AudioFormat(wav));
	fwriteint  (fp, cs + NUMCHANNELS, GetWAVFILE_FMT_NumChannels(wav));
	fwritelong (fp, cs + SAMPLERATE, GetWAVFILE_FMT_SampleRate(wav));
	fwritelong (fp, cs + BYTERATE, GetWAVFILE_FMT_ByteRate(wav));
	fwriteint  (fp, cs + BLOCKALIGN, GetWAVFILE_FMT_BlockAlign(wav));
	fwriteint  (fp, cs + BITSPERSAMPLE, GetWAVFILE_FMT_BitsPerSample(wav));

	// Write DATA Chunk (Header only)
	cs = GetWAVFILE_DATA_Offset(wav);
	fwritebytes(fp, cs + CHUNK_ID, "data", 4);
	fwritelong (fp, cs + CHUNK_SIZE, GetWAVFILE_DATA_ChunkSize(wav));

	return(wav);
}

WAVFILE *ReadWAVEFILE_Header(WAVFILE *wav, FILE *fp)
{
	long cs; // Chunk Start

	if( (NULL == fp) || (NULL == wav) )
		return(NULL);

	// Read RIFF Header
	if(0 == FindChunk(fp, "RIFF"))
		return(NULL);
	cs = 0;
	SetWAVFILE_RIFF_Filesize(wav, freadlong(fp, cs + CHUNK_SIZE));

	// Read FORMAT Chunk
	cs = SetWAVFILE_FMT_Offset(wav, FindChunk(fp, "fmt "));
	if(0 == cs)
		return(NULL);
	SetWAVFILE_FMT_ChunkSize(wav, freadlong (fp, cs + CHUNK_SIZE));
	SetWAVFILE_FMT_AudioFormat(wav, freadint  (fp, cs + AUDIOFORMAT));
	SetWAVFILE_FMT_NumChannels(wav, freadint  (fp, cs + NUMCHANNELS));
	SetWAVFILE_FMT_SampleRate(wav, freadlong (fp, cs + SAMPLERATE));
	SetWAVFILE_FMT_ByteRate(wav, freadlong (fp, cs + BYTERATE));
	SetWAVFILE_FMT_BlockAlign(wav, freadint  (fp, cs + BLOCKALIGN));
	SetWAVFILE_FMT_BitsPerSample(wav, freadint  (fp, cs + BITSPERSAMPLE));

	// Write DATA Chunk (Header only)
	cs = SetWAVFILE_DATA_Offset(wav, FindChunk(fp, "data"));
	if(0 == cs)
		return(NULL);
	SetWAVFILE_DATA_ChunkSize(wav, freadlong (fp, cs + CHUNK_SIZE));

	return(wav);
}

int BytesPerSample(int bits)
{
	return( (bits/8) + (0 != bits%8) );
}

int PrepWAVFILE(WAVFILE *wav, int chan, int bits, long rate, long frames)
{
	int SampleSize, BlockAlign;
	long ByteRate, DataBytes;

	if( NULL == wav )
		return(NULL);

	SampleSize = BytesPerSample(bits);
	BlockAlign = chan * SampleSize;
	ByteRate = rate * BlockAlign;
	DataBytes = frames * BlockAlign;

	// Prepare RIFF Chunk
	SetWAVFILE_RIFF_Filesize(wav, (36 + DataBytes));

	// Prepare FORMAT Chunk
	SetWAVFILE_FMT_Offset(wav, 12);
	SetWAVFILE_FMT_ChunkSize(wav, 16);
	SetWAVFILE_FMT_AudioFormat(wav, 1);
	SetWAVFILE_FMT_NumChannels(wav, chan);
	SetWAVFILE_FMT_SampleRate(wav, rate);
	SetWAVFILE_FMT_ByteRate(wav, ByteRate);
	SetWAVFILE_FMT_BlockAlign(wav, BlockAlign);
	SetWAVFILE_FMT_BitsPerSample(wav, bits);

	// Prepare DATA Chunk (Header Only)
	SetWAVFILE_DATA_Offset(wav, 36);
	SetWAVFILE_DATA_ChunkSize(wav, DataBytes);

	return(0);
}

WAVFILE *DisplayWAVEFILE(WAVFILE *wav)
{
	BLANKLINE;
	printf("Data stored in wavefile structure:\n");

	if(NULL == wav)
	{
		printf(" - WAVFILE pointer NULL.\n");
		return(NULL);
	}

	BLANKLINE;
	printf(" RIFF Header:\n");
	printf("  File Size: 8 + %li bytes\n", GetWAVFILE_RIFF_Filesize(wav));
	BLANKLINE;
	printf(" FORMAT Chunk:\n");
	printf("  Offset in file: %li bytes\n", GetWAVFILE_FMT_Offset(wav));
	printf("  Chunk Size: 8 + %li bytes\n", GetWAVFILE_FMT_ChunkSize(wav));
	printf("  Audio Format: %i \n", GetWAVFILE_FMT_AudioFormat(wav));
	printf("  Channels: %i\n", GetWAVFILE_FMT_NumChannels(wav));
	printf("  Sample Rate : %li Hz\n", GetWAVFILE_FMT_SampleRate(wav));
	printf("  Byte Rate: %li bytes/sec\n", GetWAVFILE_FMT_ByteRate(wav));
	printf("  Block Alignment: %i bytes\n", GetWAVFILE_FMT_BlockAlign(wav));
	printf("  Bits per Sample: %i\n", GetWAVFILE_FMT_BitsPerSample(wav));
	BLANKLINE;
	printf(" DATA Chunk:\n");
	printf("  Offset in file: %li bytes\n", GetWAVFILE_DATA_Offset(wav));
	printf("  Total Data: %li bytes\n", GetWAVFILE_DATA_ChunkSize(wav));

	return(wav);
}

int WriteWAV2TXTdata(WAVFILE *wav, FILE *fpWAV, FILE *fpTXT)
{
	long BlockStart;
	int bits;
	int  SampleSize, BlockAlign;
	long frame, frames;
	int channel, channels;
	long data;
	int shift;

	if( NULL == wav )
		return(1);

	if( NULL == fpTXT )
		return(2);

	if( NULL == fpWAV )
		return(3);

	bits = GetWAVFILE_FMT_BitsPerSample(wav);
	channels = GetWAVFILE_FMT_NumChannels(wav);
	BlockAlign = GetWAVFILE_FMT_BlockAlign(wav);
	frames = GetWAVFILE_DATA_ChunkSize(wav)/BlockAlign;
	SampleSize = BytesPerSample(bits);
	shift = (8-bits%8)%8;

	BlockStart = 8 + GetWAVFILE_DATA_Offset(wav);
	for(frame = 0; frame < frames; frame++)
	{
		printf("\rWriting %li frames: %li", frames, frame);
		for(channel = 0; channel < channels; channel++)
		{
			data = freadlong(fpWAV, BlockStart + channel*SampleSize);
			data >>= shift;
			fprintf(fpTXT, "%li ", data);
		}
		fprintf(fpTXT, "\n");
		BlockStart += BlockAlign;
	}
	printf("\rWriting %li frames: %li (done)\n", frames, frame);

	return(0);
}

int WriteTXT2WAVdata(WAVFILE *wav, FILE *fpTXT, FILE *fpWAV,
											  double min, double max)
{
	long BlockStart;
	int bits;
	int  SampleSize, BlockAlign;
	long frame, frames;
	int channel, channels;
	long data, minsig, maxsig, margin;
	int shift;
	double value;

	double slope, intercept;

	if( NULL == wav )
		return(1);

	if( NULL == fpTXT )
		return(2);

	if( NULL == fpWAV )
		return(3);

	bits = GetWAVFILE_FMT_BitsPerSample(wav);

	maxsig = (bits>8)? bits-1 : bits;
	maxsig = -1 + pow(2, maxsig);
	minsig = (bits>8)? -maxsig-1 : 0;

	margin = (long) (0.1*(maxsig - minsig));
	maxsig -= margin;
	minsig += margin;

	slope = (double)(maxsig - minsig) / (max - min);
	intercept = (double) minsig - slope*min;

	channels = GetWAVFILE_FMT_NumChannels(wav);
	BlockAlign = GetWAVFILE_FMT_BlockAlign(wav);
	frames = GetWAVFILE_DATA_ChunkSize(wav)/BlockAlign;
	SampleSize = BytesPerSample(bits);
	shift = (8-bits%8)%8;

	rewind(fpTXT);
	while('\n' != fgetc(fpTXT));  // Skip first line;

	BlockStart = 8 + GetWAVFILE_DATA_Offset(wav);
	for(frame = 0; frame < frames; frame++)
	{
		printf("\rWriting %li frames: %li", frames, frame);
		for(channel = 0; channel < channels; channel++)
		{
			fscanf(fpTXT, "%lf", &value);
			data = slope*value + intercept;
			data <<= shift;
			fwritebytes(fpWAV, BlockStart + channel*SampleSize,
				(char *) &data, SampleSize);
		}
		BlockStart += BlockAlign;
	}
	printf("\rWriting %li frames: %li (done)\n", frames, frame);

	return(0);
}