/*        NorQualizer V1.2 - WIN VERSION - Automatic Equalizer / Normalizer for Audio/WAV files     *
 *         suitable for preparing music libraries and audio CDs from various MP3 file sources        */

/*         Tested and compiled with gcc via Code::Blocks IDE        */

#include <stddef.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#define doubl_targetrate	176400

// General purpose variables

char filname[1024];
char outname[1024];
char extname[5];
int dumm;
int nparser;
unsigned char makemp3out=0;
unsigned char lowqsound=0;
unsigned char force44=0;
unsigned char popdiscri=0;

// Structures/unions types:

typedef union
        {
        signed short s16b;
        struct
                {
                unsigned char lsb;
                unsigned char msb;
                } via8b;
        } t_splfull;


// WAV processing variables

const char Head[44]={'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00,0x44,0xAC,0x00,0x00,0x10,0xB1,0x02,0x00,0x04,0x00,0x10,0x00,'d','a','t','a',0,0,0,0};
unsigned char header[44];
unsigned char rattrap;
unsigned char rattrapmax;
unsigned long workvar;
signed long workvar2;
unsigned long sourcel;
unsigned long targetl;
unsigned long reserved;
unsigned long sourcepointer;
unsigned long targetpointer;
unsigned long pointopoint;
unsigned long pointstep;
unsigned long sourcerate;
unsigned char nchannel;
float xmag;
FILE *wavopen;

// Click and pop discrimination

#define POPWIDTH ((unsigned char)3)
#define alter_qpk ((unsigned char)2)
signed short pre_pkminhighleft;
signed short pre_pkminhighright;
signed short pre_pkmaxhighleft;
signed short pre_pkmaxhighright;
unsigned char spikewidth_r_up;
unsigned char spikewidth_r_low;
unsigned char spikewidth_l_up;
unsigned char spikewidth_l_low;


// EQ filter-detector variables

#define lowfreql ((float)0.013)
#define lowfreqh ((float)0.710)
#define twopasscriteria ((float)4)
#define default_qpk ((unsigned char)8)

char *sample;

t_splfull splowleft;
t_splfull splowright;
t_splfull spmidleft;
t_splfull spmidright;
t_splfull sphighleft;
t_splfull sphighright;
t_splfull tmp16s;

signed short pkminlowleft;
signed short pkminlowright;
signed short pkmaxlowleft;
signed short pkmaxlowright;
signed short quasipkminlowleft;
signed short quasipkminlowright;
signed short quasipkmaxlowleft;
signed short quasipkmaxlowright;

signed short pkminmidleft;
signed short pkminmidright;
signed short pkmaxmidleft;
signed short pkmaxmidright;
signed short quasipkminmidleft;
signed short quasipkminmidright;
signed short quasipkmaxmidleft;
signed short quasipkmaxmidright;

signed short pkminhighleft;
signed short pkminhighright;
signed short pkmaxhighleft;
signed short pkmaxhighright;
signed short quasipkminhighleft;
signed short quasipkminhighright;
signed short quasipkmaxhighleft;
signed short quasipkmaxhighright;

unsigned char leftupcnt=0;
unsigned char rightupcnt=0;
unsigned char leftdowncnt=0;
unsigned char rightdowncnt=0;

float averagcoeff;


// EQ parameter-related variables

float leftampratelow;
float leftampratemid;
float leftampratehigh;
float rightampratelow;
float rightampratemid;
float rightampratehigh;

float requiredlb=17000;
float requiredmb=13000;
float requiredhb=4500;

float tmpflt;

unsigned char passcounter;
unsigned char passnumber;

int main(int argc,char *argv[])
	{
	for(dumm=0;dumm!=1024;dumm++)
		{
		filname[dumm]=0;
		outname[dumm]=0;
		}
	printf("\n\nCommand Line NorQualizer V1.2 - WIN Version - V.Portet 2008-2013\nSmart automatic equalizer / normalizer\n\n");
	if (argv[1]!=NULL)
		{
		if ((!strcmp(argv[1],"-h"))||(!strcmp(argv[1],"--h")))
			{
			printf("WAV and Audio file processing tool  -  Help:\n\n- Without command line parameters:\n  Automatically ask file name and generates only WAV file as output.\n");
			printf("- With command line parameters: norcom [inputfile] <options>\n\n");
			printf("  Available options:\n");
			printf("  '-h' : Display the present help\n");
			printf("  '-r' : Encode / Re-encode result to MP3\n");
			printf("  '-b' : Reduce bass profile (for tracks without basslines only)\n");
			printf("  '-f' : Force file loading, assuming that the stream is at 44100 spl/s stereo, big endian\n");
			printf("  '-v' : Enable click and pop discriminator (allow clamping of spikes - To be used for vinyl records)\n\n");
			return 0;
			}
		strncpy(filname,argv[1],1020);
		}
	else
		{
		printf("Select the file you want to process or simply hit Q + <return> to quitt\n");
		filname[0]=0;
		printf("\nWAV File to be processed : ");
		scanf("%1020s",filname);
		}
	if (strlen(filname)<5)
		{
		printf("\nImproper file name ! Aborting.\n\n");
		return 0;
		}
	dumm=2;
	while (argv[dumm]!=NULL)
		{
		if ((strlen(argv[dumm])==2)&&(argv[dumm][0]=='-'))
			{
			// Did the user request MP3 encoding after processing ? Did he select low bass profile ? Did he force WAV loading ? Did he enable clic and pop discriminator ?
			if ((argv[dumm][1]=='r')||(argv[dumm][1]=='R'))
				{
				makemp3out=1;
				}
			if ((argv[dumm][1]=='b')||(argv[dumm][1]=='B'))
				{
				requiredlb=9000;
				}
			if ((argv[dumm][1]=='f')||(argv[dumm][1]=='F'))
				{
				force44=1;
				}
			if ((argv[dumm][1]=='v')||(argv[dumm][1]=='V'))
				{
				popdiscri=1;
				}
			}
		else
			{
			printf("\nWARNING: Unknown parameter %s has been ignored !\n",argv[dumm]);
			}
		dumm++;
		}

	// First check if file exists
	wavopen=fopen(filname,"rb");
	if (wavopen)
		{
		// Check if extension is WAV
		dumm=strlen(filname)-4;
		strncpy(extname,&filname[dumm],5);
		if (((extname[1]!='w')&&(extname[1]!='W'))||((extname[2]!='a')&&(extname[2]!='A'))||((extname[3]!='v')&&(extname[3]!='V')))
			{
			// Extension is not WAV.
			printf("This file is not a WAV file. Use appropriate BAT file and install FFMPEG to be able to process MP3 or other multimedia files!\n");
			fclose(wavopen);
			return 0;
			}
		else
			{
			// It is assumed to be a WAV file. Check it.
			printf("\nProcessing %s file...\n\n",extname);
			fseek(wavopen,0,SEEK_END);
			sourcel=ftell(wavopen);
			if (sourcel>1024)
				{
				printf("Opened - File length: %ld\n",sourcel);
				sourcel-=44;
				fseek(wavopen,0,SEEK_SET);
				fread(&header[0],44,1,wavopen);
				if ((header[0]!='R')||(header[1]!='I')||(header[2]!='F')||(header[3]!='F'))
					{
					fclose(wavopen);
					printf("This is not a valid WAV file at all !\n");
					return 0;
					}
				// This is a true WAV file.
				// Does this WAV file have to be resampled ?
				// Computing expansion/shrinking rate
				if (force44==1)
					{
					xmag = (float)1;
					}
				else
					{
					nchannel = header[22];
					if (nchannel==2)
						{
						sourcerate = ((unsigned long) header[24]) + (((unsigned long) header[25])<<8) + (((unsigned long) header[26])<<16) + (((unsigned long) header[27])<<24);
						}
					else
						{
						if (nchannel==1)
							{
							sourcerate = ((unsigned long) header[28]) + (((unsigned long) header[29])<<8) + (((unsigned long) header[30])<<16) + (((unsigned long) header[31])<<24);
							}
						else
							{
							fclose(wavopen);
							printf("WAV header data seems to be corrupted. Check or try by using the -f option to load as raw data.\n");
							return 0;
							}
						}
					// Shrinking / expanding rate
					xmag = doubl_targetrate / ( ((float) sourcerate) * ( 3 * (nchannel - 1) + 1 ) );
					}

				// Deducing target length
				targetl = (((unsigned long) (xmag * sourcel))>>2)<<2;
				// Reserved memory
				if (targetl>sourcel)
					{
					reserved = 3*targetl;
					}
				else
					{
					reserved = (2*targetl) + sourcel;
					}
				printf("Reserving workspace of %ld bytes...\n",reserved);
				// sample = (unsigned char __huge *)malloc(reserved);
				sample = (char *)malloc(reserved);
				// Pass ?
				if (sample == NULL)
					{
					printf("Error: Not enough allocated memory !\n");
					printf("source length = %ld - target length = %ld - required space = %ld\n",sourcel,targetl,reserved);
					fclose(wavopen);
					return 0;
					}
				// Need to resample ?
				if ((xmag>0.95)&&(xmag<1.05))
					{
					printf("\nStep 1: This file does not need to be resampled...\n");
					// Do a simple copy in the buffer...
					printf("Loading in buffer...\n");
					fread(&sample[0],1,sourcel,wavopen);
					fclose(wavopen);
					targetl=sourcel;
					}
				else
					{
					// Load file
					printf("Step 1: File resampling...\n");
					fread(&sample[0],sourcel,1,wavopen);
					fclose(wavopen);
					// And now....Sampling rate conversion process:
					rattrap=0;
					pointstep = ( ( (unsigned long) (4*xmag) ) >> 2 ) << 2;
					if (pointstep==0)
						{
						pointstep=4;
						rattrapmax=4;
						}
					else
						{
						if (pointstep<=8)
							{
							rattrapmax=32;
							}
						else
							{
							rattrapmax=64;
							}
						}
					if (targetl<sourcel)
						{
						// Shrinking process
						printf("Shrinking...\n");
						sourcepointer=0;
						targetpointer=0;
						while (sourcepointer != sourcel)
							{
							rattrap+=1;
							if (rattrap==rattrapmax)
								{
								pointopoint = (((unsigned long) (xmag * ((float) sourcepointer)))>>2)<<2;
								rattrap=0;
								}
							else
								{
								pointopoint = targetpointer + pointstep;
								}
							while (targetpointer<pointopoint)
								{
								if (targetpointer!=sourcepointer)
									{
									sample[targetpointer]=sample[sourcepointer];
									sample[targetpointer+1]=sample[sourcepointer+1];
									sample[targetpointer+2]=sample[sourcepointer+2];
									sample[targetpointer+3]=sample[sourcepointer+3];
									}
								targetpointer += 4;
								}
							sourcepointer += 4;
							}
						// Adjusting exact file length (if too large, will be systematically truncated)
						// Fill the eventual end gap with zeroes
						while (targetpointer<targetl)
							{
							sample[targetpointer]=0;
							sample[targetpointer+1]=0;
							sample[targetpointer+2]=0;
							sample[targetpointer+3]=0;
							targetpointer += 4;
							}
						}
					else
						{
						// Expanding process
						printf("Expanding...\n");
						sourcepointer=sourcel;
						targetpointer=targetl;
						while ((sourcepointer>=4)&&(targetpointer>=4))  // targetpointer is monitored for safety
							{
							sourcepointer -= 4;
							rattrap+=1;
							if (rattrap==rattrapmax)
								{
								pointopoint = (((unsigned long) (xmag * ((float) sourcepointer)))>>2)<<2;
								rattrap=0;
								}
							else
								{
								if (pointstep < targetpointer)
									{
									pointopoint = targetpointer - pointstep;
									if (pointopoint < 4)
										{
										pointopoint=4;
										}
									}
								else
									{
									pointopoint=4;
									}
								}
							while (targetpointer>pointopoint)
								{
								targetpointer -= 4;
								if (targetpointer!=sourcepointer)
									{
									sample[targetpointer]=sample[sourcepointer];
									sample[targetpointer+1]=sample[sourcepointer+1];
									sample[targetpointer+2]=sample[sourcepointer+2];
									sample[targetpointer+3]=sample[sourcepointer+3];
									}
								}
							}
						// Adjusting exact file length (if too large, will be systematically truncated)
						// Fill the eventual end gap with zeroes
						while (targetpointer>60)
							{
							targetpointer -= 4;
							sample[targetpointer]=0;
							sample[targetpointer+1]=0;
							sample[targetpointer+2]=0;
							sample[targetpointer+3]=0;
							}
						}
					}
				}
			else
				{
				printf("This sound file is a joke !\n");
				fclose(wavopen);
				return 0;
				}
			}

		// Initialize 3-band filter
		passnumber=1;
		passcounter=0;
		while (passcounter<passnumber)
			{
			// Initial conditions
			splowleft.via8b.lsb = sample[0];
			splowleft.via8b.msb = sample[1];
			spmidleft.via8b.lsb = sample[0];
			spmidleft.via8b.msb = sample[1];
			splowright.via8b.lsb = sample[2];
			splowright.via8b.msb = sample[3];
			spmidright.via8b.lsb = sample[2];
			spmidright.via8b.msb = sample[3];
			pkminhighleft = 0;
			pkminhighright = 0;
			pkmaxhighleft = 0;
			pkmaxhighright = 0;
			quasipkminhighleft = 0;
			quasipkminhighright = 0;
			quasipkmaxhighleft = 0;
			quasipkmaxhighright = 0;
            pkminlowleft = 0;
            pkminlowright = 0;
            pkmaxlowleft = 0;
            pkmaxlowright = 0;
            quasipkminlowleft = 0;
            quasipkminlowright = 0;
            quasipkmaxlowleft = 0;
            quasipkmaxlowright = 0;
            pkminmidleft = 0;
            pkminmidright = 0;
            pkmaxmidleft = 0;
            pkmaxmidright = 0;
            quasipkminmidleft = 0;
            quasipkminmidright = 0;
            quasipkmaxmidleft = 0;
            quasipkmaxmidright = 0;
            pre_pkminhighleft = 0;
            pre_pkminhighright = 0;
            pre_pkmaxhighleft = 0;
            pre_pkmaxhighright = 0;
            spikewidth_r_up = 0;
            spikewidth_r_low = 0;
            spikewidth_l_up = 0;
            spikewidth_l_low = 0;
			leftupcnt = 0;
			leftdowncnt = 0;
			rightupcnt = 0;
			rightdowncnt = 0;

			if (passcounter!=0)
				{
				printf("\nStep 1: Restarting from previously processed track in first pass...\n\n");
				}

			printf("\nStep 2: three-band filter and scanning...\n");
			// *** Filter and scan the 3 EQ bands ***
			for (targetpointer=0;targetpointer<targetl;targetpointer+=4)
				{
				// Display points on progress...
				if ((targetpointer&0x00FFFFFF)==0)
					{
					printf("...\n");
					}
				// *** Left ch. ***
				tmp16s.via8b.lsb = sample[targetpointer];
				tmp16s.via8b.msb = sample[targetpointer+1];
				// (Safety against clipping-on-filtering for high amplitude audio)
				tmp16s.s16b = (signed short)((float)0.85*((float)tmp16s.s16b));

				// Low cut-off => Low band
				splowleft.s16b += (signed short)(lowfreql * (float)(tmp16s.s16b - splowleft.s16b));

				// High cut-off
				spmidleft.s16b += (signed short)(lowfreqh * (float)(tmp16s.s16b - spmidleft.s16b));

				// Deducing middle and high band
				workvar2 = (signed long)tmp16s.s16b - (signed long)spmidleft.s16b; 	// High band
				if (workvar2 > 32766)
					{
					workvar2 = 32766;		// (Might clipp on differential process)
					}
				if (workvar2 < -32766)
					{
					workvar2 = -32766;
					}
				sphighleft.s16b = (signed short)workvar2;
				workvar2 = (signed long)spmidleft.s16b - (signed long)splowleft.s16b;	// Middle band
				if (workvar2 > 32766)
					{
					workvar2 = 32766;		// (Might clipp on differential process)
					}
				if (workvar2 < -32766)
					{
					workvar2 = -32766;
					}
				tmp16s.s16b = (signed short)workvar2;

				// Save to band streams
				sample[targetl+targetpointer] = splowleft.via8b.lsb;
				sample[targetl+targetpointer+1] = splowleft.via8b.msb;
				sample[targetpointer] = tmp16s.via8b.lsb;
				sample[targetpointer+1] = tmp16s.via8b.msb;
				sample[(targetl*2)+targetpointer] = sphighleft.via8b.lsb;
				sample[(targetl*2)+targetpointer+1] = sphighleft.via8b.msb;

				// Peak detectors
				if (splowleft.s16b > pkmaxlowleft)
					{
					pkmaxlowleft = splowleft.s16b;
					}
				if (splowleft.s16b < pkminlowleft)
					{
					pkminlowleft = splowleft.s16b;
					}
				if (tmp16s.s16b > pkmaxmidleft)
					{
					pkmaxmidleft = tmp16s.s16b;
					}
				if (tmp16s.s16b < pkminmidleft)
					{
					pkminmidleft = tmp16s.s16b;
					}
				if (popdiscri==0)
					{
					// Case of classical peak detection
					if (sphighleft.s16b > pkmaxhighleft)
						{
						pkmaxhighleft = sphighleft.s16b;
						}
					if (sphighleft.s16b < pkminhighleft)
						{
						pkminhighleft = sphighleft.s16b;
						}
					}
				else
					{
					// Case of click and pop discrimination (conditionnal peak detection)
					if (sphighleft.s16b > pkmaxhighleft)
						{
						if (spikewidth_l_up == 0)
							{
							pre_pkmaxhighleft = sphighleft.s16b;
							}
						else
							{
							if (sphighleft.s16b > pre_pkmaxhighleft)
								{
								pre_pkmaxhighleft = sphighleft.s16b;
								}
							}
						spikewidth_l_up++;
						if (spikewidth_l_up > POPWIDTH)
							{
							pkmaxhighleft = pre_pkmaxhighleft;
							spikewidth_l_up = 0;
							}
						}
					else
						{
						spikewidth_l_up = 0;
						}
					if (sphighleft.s16b < pkminhighleft)
						{
						if (spikewidth_l_low == 0)
							{
							pre_pkminhighleft = sphighleft.s16b;
							}
						else
							{
							if (sphighleft.s16b < pre_pkminhighleft)
								{
								pre_pkminhighleft = sphighleft.s16b;
								}
							}
						spikewidth_l_low++;
						if (spikewidth_l_low > POPWIDTH)
							{
							pkminhighleft = pre_pkminhighleft;
							spikewidth_l_low = 0;
							}
						}
					else
						{
						spikewidth_l_low = 0;
						}
					}

				// Quasi-peak detectors
				if (splowleft.s16b > quasipkmaxlowleft)
					{
					quasipkmaxlowleft += (splowleft.s16b - quasipkmaxlowleft)/((signed short)1000);
					}

				if (splowleft.s16b < quasipkminlowleft)
					{
					quasipkminlowleft += (splowleft.s16b - quasipkminlowleft)/((signed short)1000);
					}

				if (tmp16s.s16b > (pkmaxmidleft/((signed short)2)))
					{
					quasipkmaxmidleft = (tmp16s.s16b)/2 + (quasipkmaxmidleft)/2;
					}

				if (tmp16s.s16b < (pkminmidleft/((signed short)2)))
					{
					quasipkminmidleft = (tmp16s.s16b)/2 + (quasipkminmidleft)/2;
					}

				if (sphighleft.s16b > quasipkmaxhighleft)
					{
					if ((popdiscri==1)&&(spikewidth_l_up!=0))
						{
						// Case of click and pop specific evaluation
						leftupcnt += alter_qpk;
						}
					else
						{
						// Normal case
						leftupcnt += default_qpk;
						}
					if (leftupcnt == 0)
						{
						quasipkmaxhighleft++;
						}
					}

				if (sphighleft.s16b < quasipkminhighleft)
					{
					if ((popdiscri==1)&&(spikewidth_l_low!=0))
						{
						// Case of click and pop specific evaluation
						leftdowncnt += alter_qpk;
						}
					else
						{
						// Normal case
						leftdowncnt += default_qpk;
						}
					if (leftdowncnt == 0)
						{
						quasipkminhighleft--;
						}
					}

				// *** Right ch. ***
				tmp16s.via8b.lsb = sample[targetpointer+2];
				tmp16s.via8b.msb = sample[targetpointer+3];
				// (Safety against clipping-on-filtering for high amplitude audio)
				tmp16s.s16b = (signed short)((float)0.85*((float)tmp16s.s16b));

				// Low cut-off => Low band
				splowright.s16b += (signed short)(lowfreql * (float)(tmp16s.s16b - splowright.s16b));

				// High cut-off
				spmidright.s16b += (signed short)(lowfreqh * (float)(tmp16s.s16b - spmidright.s16b));

				// Deducing middle and high band
				workvar2 = (signed long)tmp16s.s16b - (signed long)spmidright.s16b; 	// High band
				if (workvar2 > 32766)
					{
					workvar2 = 32766;		// (Might clipp on differential process)
					}
				if (workvar2 < -32766)
					{
					workvar2 = -32766;
					}
				sphighright.s16b = (signed short)workvar2;
				workvar2 = (signed long)spmidright.s16b - (signed long)splowright.s16b;	// Middle band
				if (workvar2 > 32766)
					{
					workvar2 = 32766;		// (Might clipp on differential process)
					}
				if (workvar2 < -32766)
					{
					workvar2 = -32766;
					}
				tmp16s.s16b = (signed short)workvar2;
				// Save to band streams
				sample[targetl+targetpointer+2] = splowright.via8b.lsb;
				sample[targetl+targetpointer+3] = splowright.via8b.msb;
				sample[targetpointer+2] = tmp16s.via8b.lsb;
				sample[targetpointer+3] = tmp16s.via8b.msb;
				sample[(targetl*2)+targetpointer+2] = sphighright.via8b.lsb;
				sample[(targetl*2)+targetpointer+3] = sphighright.via8b.msb;

				// Peak detectors
				if (splowright.s16b > pkmaxlowright)
					{
					pkmaxlowright = splowright.s16b;
					}
				if (splowright.s16b < pkminlowright)
					{
					pkminlowright = splowright.s16b;
					}
				if (tmp16s.s16b > pkmaxmidright)
					{
					pkmaxmidright = tmp16s.s16b;
					}
				if (tmp16s.s16b < pkminmidright)
					{
					pkminmidright = tmp16s.s16b;
					}
				if (popdiscri==0)
					{
					// Case of the classical peak detection
					if (sphighright.s16b > pkmaxhighright)
						{
						pkmaxhighright = sphighright.s16b;
						}
					if (sphighright.s16b < pkminhighright)
						{
						pkminhighright = sphighright.s16b;
						}
					}
				else
					{
					// Case of click and pop discrimination (conditionnal peak detection)
					if (sphighright.s16b > pkmaxhighright)
						{
						if (spikewidth_r_up == 0)
							{
							pre_pkmaxhighright = sphighright.s16b;
							}
						else
							{
							if (sphighright.s16b > pre_pkmaxhighright)
								{
								pre_pkmaxhighright = sphighright.s16b;
								}
							}
						spikewidth_r_up++;
						if (spikewidth_r_up > POPWIDTH)
							{
							pkmaxhighright = pre_pkmaxhighright;
							spikewidth_r_up = 0;
							}
						}
					else
						{
						spikewidth_r_up = 0;
						}
					if (sphighright.s16b < pkminhighright)
						{
						if (spikewidth_r_low == 0)
							{
							pre_pkminhighright = sphighright.s16b;
							}
						else
							{
							if (sphighright.s16b < pre_pkminhighright)
								{
								pre_pkminhighright = sphighright.s16b;
								}
							}
						spikewidth_r_low++;
						if (spikewidth_r_low > POPWIDTH)
							{
							pkminhighright = pre_pkminhighright;
							spikewidth_r_low = 0;
							}
						}
					else
						{
						spikewidth_r_low = 0;
						}
					}

				// Quasi-peak detectors
				if (splowright.s16b > quasipkmaxlowright)
					{
					quasipkmaxlowright += (splowright.s16b - quasipkmaxlowright)/((signed short)1000);
					}

				if (splowright.s16b < quasipkminlowright)
					{
					quasipkminlowright += (splowright.s16b - quasipkminlowright)/((signed short)1000);
					}

				if (tmp16s.s16b > (pkmaxmidright/((signed short)2)))
					{
					quasipkmaxmidright = (tmp16s.s16b)/2 + (quasipkmaxmidright)/2;
					}

				if (tmp16s.s16b < (pkminmidright/((signed short)2)))
					{
					quasipkminmidright = (tmp16s.s16b)/2 + (quasipkminmidright)/2;
					}
				if (sphighright.s16b > quasipkmaxhighright)
					{
					if ((popdiscri==1)&&(spikewidth_r_up!=0))
						{
						// Case of click and pop specific evaluation
						rightupcnt += alter_qpk;
						}
					else
						{
						// Normal case
						rightupcnt += default_qpk;
						}
					if (rightupcnt == 0)
						{
						quasipkmaxhighright++;
						}
					}

				if (sphighright.s16b < quasipkminhighright)
					{
					if ((popdiscri==1)&&(spikewidth_r_low!=0))
						{
						// Case of click and pop specific evaluation
						rightdowncnt += alter_qpk;
						}
					else
						{
						// Normal case
						rightdowncnt += default_qpk;
						}
					if (rightdowncnt == 0)
						{
						quasipkminhighright--;
						}
					}
				}

			printf("Monitored quasi-peaks:\n");
			printf("LF: R = %d %d -- L = %d %d\n",quasipkminlowright,quasipkmaxlowright,quasipkminlowleft,quasipkmaxlowleft);
			printf("MF: R = %d %d -- L = %d %d\n",quasipkminmidright,quasipkmaxmidright,quasipkminmidleft,quasipkmaxmidleft);
			printf("HF: R = %d %d -- L = %d %d\n\n",quasipkminhighright,quasipkmaxhighright,quasipkminhighleft,quasipkmaxhighleft);
			printf("Monitored peaks:\n");
			printf("LF: R = %d %d -- L = %d %d\n",pkminlowright,pkmaxlowright,pkminlowleft,pkmaxlowleft);
			printf("MF: R = %d %d -- L = %d %d\n",pkminmidright,pkmaxmidright,pkminmidleft,pkmaxmidleft);
			printf("HF: R = %d %d -- L = %d %d\n\n",pkminhighright,pkmaxhighright,pkminhighleft,pkmaxhighleft);

			// *** Define normalization for each band ***
			printf("Step 3: Setting correction parameters...\n");

			// Low band left
			if (quasipkmaxlowleft < 0)
				{
				quasipkmaxlowleft = 0;
				}
			if ((quasipkminlowleft < 0)&&((-quasipkminlowleft) > quasipkmaxlowleft))
				{
				quasipkmaxlowleft = -quasipkminlowleft;
				}
			if (quasipkmaxlowleft > (signed short)(requiredlb / ((float)40)))
				{
				leftampratelow = requiredlb / ((float)quasipkmaxlowleft);
				}
			else
				{
				printf("Warning: Low frequency correction on left channel limited at maximum (+32dB).\n");
				leftampratelow = (float)40;
				}

			// Low band right
			if (quasipkmaxlowright < 0)
				{
				quasipkmaxlowright = 0;
				}
			if ((quasipkminlowright < 0)&&((-quasipkminlowright) > quasipkmaxlowright))
				{
				quasipkmaxlowright = -quasipkminlowright;
				}
			if (quasipkmaxlowright > (signed short)(requiredlb / ((float)40)))
				{
				rightampratelow = requiredlb / ((float)quasipkmaxlowright);
				}
			else
				{
				printf("Warning: Low frequency correction on right channel limited at maximum (+32dB).\n");
				rightampratelow = (float)40;
				}

			// Middle band left
			if (quasipkmaxmidleft < 0)
				{
				quasipkmaxmidleft = 0;
				}
			if ((quasipkminmidleft < 0)&&((-quasipkminmidleft) > quasipkmaxmidleft))
				{
				quasipkmaxmidleft = -quasipkminmidleft;
				}
			if (quasipkmaxmidleft > (signed short)(requiredmb / ((float)10)))
				{
				leftampratemid = requiredmb / ((float)quasipkmaxmidleft);
				}
			else
				{
				printf("Warning: Middle frequency correction on left channel limited at maximum (+20dB).\n");
				leftampratemid = (float)10;
				}

			// Middle band right
			if (quasipkmaxmidright < 0)
				{
				quasipkmaxmidright = 0;
				}
			if ((quasipkminmidright < 0)&&((-quasipkminmidright) > quasipkmaxmidright))
				{
				quasipkmaxmidright = -quasipkminmidright;
				}
			if (quasipkmaxmidright > (signed short)(requiredmb / ((float)10)))
				{
				rightampratemid = requiredmb / ((float)quasipkmaxmidright);
				}
			else
				{
				printf("Warning: Middle frequency correction on right channel limited at maximum (+20dB).\n");
				rightampratemid = (float)10;
				}

			// High band left
			if (quasipkmaxhighleft < 0)
				{
				quasipkmaxhighleft = 0;
				}
			if ((quasipkminhighleft < 0)&&((-quasipkminhighleft) > quasipkmaxhighleft))
				{
				quasipkmaxhighleft = -quasipkminhighleft;
				}
			if (quasipkmaxhighleft > (signed short)(requiredhb / ((float)56)))
				{
				leftampratehigh = requiredhb / ((float)quasipkmaxhighleft);
				}
			else
				{
				printf("Warning: High frequency correction on left channel limited at maximum (+35dB).\n");
				leftampratehigh = (float)56;
				lowqsound = 1;
				}

			// High band right
			if (quasipkmaxhighright < 0)
				{
				quasipkmaxhighright = 0;
				}
			if ((quasipkminhighright < 0)&&((-quasipkminhighright) > quasipkmaxhighright))
				{
				quasipkmaxhighright = -quasipkminhighright;
				}
			if (quasipkmaxhighright > (signed short)(requiredhb / ((float)56)))
				{
				rightampratehigh = requiredhb / ((float)quasipkmaxhighright);
				}
			else
				{
				printf("Warning: High frequency correction on right channel limited at maximum (+35dB).\n");
				rightampratehigh = (float)56;
				lowqsound = 1;
				}

			printf("Before max peak considerations:\n");
			printf("Left channel low freq: %f\n",leftampratelow);
			printf("Right channel low freq: %f\n",rightampratelow);
			printf("Left channel middle freq: %f\n",leftampratemid);
			printf("Right channel middle freq: %f\n",rightampratemid);
			printf("Left channel high freq: %f\n",leftampratehigh);
			printf("Right channel high freq: %f\n\n",rightampratehigh);

			// Checking for peak saturations
			// Left

			if (leftampratelow > 1)
				{
				if (pkmaxlowleft > (signed short)(((float)32767)/leftampratelow))
					{
					leftampratelow = ((float)32700)/((float)pkmaxlowleft);
					}

				if ((-pkminlowleft) > (signed short)(((float)32767)/leftampratelow))
					{
					leftampratelow = ((float)32700)/((float)(-pkminlowleft));
					}
				}
			if (leftampratemid > 1)
				{
				if (pkmaxmidleft > (signed short)(((float)32767)/leftampratemid))
					{
					leftampratemid = ((float)32700)/((float)pkmaxmidleft);
					}

				if ((-pkminmidleft) > (signed short)(((float)32767)/leftampratemid))
					{
					leftampratemid = ((float)32700)/((float)(-pkminmidleft));
					}
				}
			if (leftampratehigh > 1)
				{
				if (pkmaxhighleft > (signed short)(((float)32767)/leftampratehigh))
					{
					leftampratehigh = ((float)32700)/((float)pkmaxhighleft);
					}

				if ((-pkminhighleft) > (signed short)(((float)32767)/leftampratehigh))
					{
					leftampratehigh = ((float)32700)/((float)(-pkminhighleft));
					}
				}

			// Right

			if (rightampratelow > 1)
				{
				if (pkmaxlowright > (signed short)(((float)32767)/rightampratelow))
					{
					rightampratelow = ((float)32700)/((float)pkmaxlowright);
					}

				if ((-pkminlowright) > (signed short)(((float)32767)/rightampratelow))
					{
					rightampratelow = ((float)32700)/((float)(-pkminlowright));
					}
				}

			if (rightampratemid > 1)
				{
				if (pkmaxmidright > (signed short)(((float)32767)/rightampratemid))
					{
					rightampratemid = ((float)32700)/((float)pkmaxmidright);
					}

				if ((-pkminmidright) > (signed short)(((float)32767)/rightampratemid))
					{
					rightampratemid = ((float)32700)/((float)(-pkminmidright));
					}
				}
			if (rightampratehigh > 1)
				{
				if (pkmaxhighright > (signed short)(((float)32767)/rightampratehigh))
					{
					rightampratehigh = ((float)32700)/((float)pkmaxhighright);
					}

				if ((-pkminhighright) > (signed short)(((float)32767)/rightampratehigh))
					{
					rightampratehigh = ((float)32700)/((float)(-pkminhighright));
					}
				}
			printf("After max peak considerations");
			if (popdiscri==0)
				{
				printf(":\n");
				}
			else
				{
				printf(" (Clicks and pops not taken into account):\n");
				}
			printf("Left channel low freq: %f\n",leftampratelow);
			printf("Right channel low freq: %f\n",rightampratelow);
			printf("Left channel middle freq: %f\n",leftampratemid);
			printf("Right channel middle freq: %f\n",rightampratemid);
			printf("Left channel high freq: %f\n",leftampratehigh);
			printf("Right channel high freq: %f\n\n",rightampratehigh);

			// Criteria for double pass process (Since V0.40)
			if (passcounter==0)
				{
				if ((leftampratelow>(leftampratemid*twopasscriteria))||(leftampratelow<(leftampratemid/twopasscriteria))||(rightampratelow>(rightampratemid*twopasscriteria))||(rightampratelow<(rightampratemid/twopasscriteria)))
					{
					passnumber=2;
					}
				if ((leftampratehigh>(leftampratemid*twopasscriteria))||(leftampratehigh<(leftampratemid/twopasscriteria))||(rightampratehigh>(rightampratemid*twopasscriteria))||(rightampratehigh<(rightampratemid/twopasscriteria)))
					{
					passnumber=2;
					}
				if (passnumber==2)
					{
					printf("Note: A double pass process will be necessary.\n\n");
					}
				}

			// Normalize and mix to final audio track
			// "Norqualization" now
			printf("Step 4: Applying corrections...\n\n");
			for (targetpointer=0;targetpointer<targetl;targetpointer+=4)
				{
				// Left channel
				splowleft.via8b.lsb = sample[targetl+targetpointer];
				splowleft.via8b.msb = sample[targetl+targetpointer+1];
				spmidleft.via8b.lsb = sample[targetpointer];
				spmidleft.via8b.msb = sample[targetpointer+1];
				sphighleft.via8b.lsb = sample[(targetl*2)+targetpointer];
				sphighleft.via8b.msb = sample[(targetl*2)+targetpointer+1];

				tmpflt = leftampratelow * ((float)splowleft.s16b);
				tmpflt += leftampratemid * ((float)spmidleft.s16b);
				tmpflt += leftampratehigh * ((float)sphighleft.s16b);
				if (tmpflt > (float)32766)
					{
					if (popdiscri==0)
						{
						// On-the-fly anti-clipping by reduction of coefficients
						averagcoeff = ((float)32764)/tmpflt;
						leftampratelow *= averagcoeff;
						leftampratemid *= averagcoeff;
						leftampratehigh *= averagcoeff;
						}
					tmpflt = (float)32766;
					}
				if (tmpflt < (float)(-32766))
					{
					if (popdiscri==0)
						{
						// On-the-fly anti-clipping by reduction of coefficients
						averagcoeff = ((float)(-32764))/tmpflt;
						leftampratelow *= averagcoeff;
						leftampratemid *= averagcoeff;
						leftampratehigh *= averagcoeff;
						}
					tmpflt = (float)(-32766);
					}
				spmidleft.s16b = (signed short)tmpflt;
				sample[targetpointer] = spmidleft.via8b.lsb;
				sample[targetpointer+1] = spmidleft.via8b.msb;

				// Right channel
				splowright.via8b.lsb = sample[targetl+targetpointer+2];
				splowright.via8b.msb = sample[targetl+targetpointer+3];
				spmidright.via8b.lsb = sample[targetpointer+2];
				spmidright.via8b.msb = sample[targetpointer+3];
				sphighright.via8b.lsb = sample[(targetl*2)+targetpointer+2];
				sphighright.via8b.msb = sample[(targetl*2)+targetpointer+3];

				tmpflt = rightampratelow * ((float)splowright.s16b);
				tmpflt += rightampratemid * ((float)spmidright.s16b);
				tmpflt += rightampratehigh * ((float)sphighright.s16b);
				if (tmpflt > (float)32766)
					{
					if (popdiscri==0)
						{
						// On-the-fly anti-clipping by reduction of coefficients
						averagcoeff = ((float)32764)/tmpflt;
						rightampratelow *= averagcoeff;
						rightampratemid *= averagcoeff;
						rightampratehigh *= averagcoeff;
						}
					tmpflt = (float)32766;
					}
				if (tmpflt < (float)(-32766))
					{
					if (popdiscri==0)
						{
						// On-the-fly anti-clipping by reduction of coefficients
						averagcoeff = ((float)(-32764))/tmpflt;
						rightampratelow *= averagcoeff;
						rightampratemid *= averagcoeff;
						rightampratehigh *= averagcoeff;
						}
					tmpflt = (float)(-32766);
					}
				spmidright.s16b = (signed short)tmpflt;
				sample[targetpointer+2] = spmidright.via8b.lsb;
				sample[targetpointer+3] = spmidright.via8b.msb;
				}
			passcounter++;
			}

		// Last criterion taken into account for evaluating a low quality sound (V1.10)
		if ((passnumber==2)&&((leftampratehigh>(2*twopasscriteria))||(rightampratehigh>(2*twopasscriteria))))
			{
			// After two passes trebles remain low => low Q sound
			lowqsound=1;
			}

		// Save final result as WAV
		printf("Step 5: Saving result: \n");
		printf(" - WAV file : ");
		// Updating WAV header
		for (dumm=0;dumm!=44;dumm++)
			{
			header[dumm]=Head[dumm];
			}
		workvar=targetl+36;
		header[4]=(unsigned char) workvar & 0xFF;
		header[5]=(unsigned char) (workvar >> 8) & 0xFF;
		header[6]=(unsigned char) (workvar >> 16) & 0xFF;
		header[7]=(unsigned char) (workvar >> 24) & 0xFF;
		workvar=targetl;
		header[40]=(unsigned char) workvar & 0x000000FF;
		header[41]=(unsigned char) (workvar >> 8) & 0xFF;
		header[42]=(unsigned char) (workvar >> 16) & 0xFF;
		header[43]=(unsigned char) (workvar >> 24) & 0xFF;

		// Updating result file name
		strcpy(outname,filname);
		dumm = strlen(outname);
		strcpy(&outname[dumm-4],"_eq.wav");

		// Writing file
		wavopen=fopen(outname,"wb");
		if (wavopen)
			{
			fwrite(&header[0],44,1,wavopen);
			fwrite(&sample[0],targetl,1,wavopen);
			fclose(wavopen);
			printf("Saved as %s\n",outname);
			}
		else
			{
			printf("Could not write %s !\n",outname);
			makemp3out=0;
			}
		// Free the rest of allocated memory
		free(sample);

		// Generate the MP3 file on request
		if (makemp3out==0)
			{
			printf(" - No MP3 file requested\n\n");
			wavopen=fopen("REENCODE.BAT","w");
			if (wavopen)
				{
				fprintf(wavopen,"ECHO Task ended without MP3 re-encoding\n");
				fclose(wavopen);
				}
			}
		else
			{
			printf(" - MP3 file : ");
			strcpy(filname,outname);
			strcpy(&outname[dumm-4],"_eq.mp3");
			printf("%s\n\n",outname);
			wavopen=fopen("REENCODE.BAT","w");
			if (wavopen)
				{
				if (lowqsound==0)
					{
					printf("(Saved at 128K in stereo)\n");
					printf("Encoded via FFMPEG\n\n");
					fprintf(wavopen,"ffmpeg -i %s -f mp3 -ab 128k %s\n",filname,outname);
					}
				else
					{
					printf("(A 64K bitrate in mono appears to be sufficient considering the initial quality)\n");
					printf("Encoded via FFMPEG\n\n");
					fprintf(wavopen,"ffmpeg -i %s -f mp3 -ac 1 -ab 64k %s\n",filname,outname);
					}
				fclose(wavopen);
				}
			}
		}
	else
		{
		printf("\nCould not open %s !\n\n",filname);
		}
    return 0;
	}



