/***************************************************************
 * Name:      norqualizerMain.cpp
 * Purpose:   Code for Application Frame
 * Author:    V.Portet ()
 * Created:   2018-01-29
 * Copyright: V.Portet ()
 * License:
 **************************************************************/

#include "norqualizerMain.h"
#include <wx/msgdlg.h>

//(*InternalHeaders(norqualizerFrame)
#include <wx/artprov.h>
#include <wx/bitmap.h>
#include <wx/icon.h>
#include <wx/font.h>
#include <wx/intl.h>
#include <wx/image.h>
#include <wx/string.h>
//*)
#include <wx/dir.h>
#include <iostream>
#include <stdio.h>
#include <direct.h>

//helper functions
enum wxbuildinfoformat {
    short_f, long_f };

wxString wxbuildinfo(wxbuildinfoformat format)
{
    wxString wxbuild(wxVERSION_STRING);

    if (format == long_f )
    {
#if defined(__WXMSW__)
        wxbuild << _T("-Windows");
#elif defined(__UNIX__)
        wxbuild << _T("-Linux");
#endif

#if wxUSE_UNICODE
        wxbuild << _T("-Unicode build");
#else
        wxbuild << _T("-ANSI build");
#endif // wxUSE_UNICODE
    }

    return wxbuild;
}

#define SINGLE      0
#define MULTIPLE    1
#define DEFAULT_REQUIREDLB  17000;
#define doubl_targetrate	176400
#define POPWIDTH ((unsigned char)3)
#define alter_qpk ((unsigned char)2)
#define lowfreql ((float)0.013)
#define lowfreqh ((float)0.710)
#define twopasscriteria ((float)4)
#define default_qpk ((unsigned char)8)
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];


//(*IdInit(norqualizerFrame)
const long norqualizerFrame::ID_STATICTEXT1 = wxNewId();
const long norqualizerFrame::ID_BUTTON1 = wxNewId();
const long norqualizerFrame::ID_BUTTON2 = wxNewId();
const long norqualizerFrame::ID_CHECKBOX1 = wxNewId();
const long norqualizerFrame::ID_CHECKBOX2 = wxNewId();
const long norqualizerFrame::ID_CHECKBOX3 = wxNewId();
const long norqualizerFrame::ID_BUTTON3 = wxNewId();
const long norqualizerFrame::ID_CHECKBOX4 = wxNewId();
const long norqualizerFrame::ID_GAUGE1 = wxNewId();
const long norqualizerFrame::ID_GAUGE2 = wxNewId();
const long norqualizerFrame::ID_STATICTEXT2 = wxNewId();
const long norqualizerFrame::ID_STATICTEXT3 = wxNewId();
const long norqualizerFrame::ID_CHECKBOX5 = wxNewId();
const long norqualizerFrame::ID_STATICTEXT4 = wxNewId();
const long norqualizerFrame::idMenuQuit = wxNewId();
const long norqualizerFrame::idMenuAbout = wxNewId();
const long norqualizerFrame::ID_STATUSBAR1 = wxNewId();
const long norqualizerFrame::ID_TIMER1 = wxNewId();
//*)

// Deveopper's variables
wxString path;
wxString fordisplay;
bool mode;
bool pending;
bool atleastone;
bool atleastoneok;
unsigned int n;
unsigned int numfiles;
unsigned int filindex;
unsigned int lename;
bool tracelog;
wxArrayString filenames;
unsigned char makemp3out;
unsigned int strparser;

FILE *logfile;
FILE *wavopen;
char *namebuf;
char *argstring;
char *destpath;
STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;

// Norqualizing process
unsigned long sourcel;
unsigned long targetl;
unsigned long old_reserved=0;
char *sample;
typedef union
    {
    signed short s16b;
    struct
         {
         unsigned char lsb;
         unsigned char msb;
         } via8b;
    } t_splfull;
unsigned long sourcerate;
unsigned char nchannel;
float xmag;



BEGIN_EVENT_TABLE(norqualizerFrame,wxFrame)
    //(*EventTable(norqualizerFrame)
    //*)
END_EVENT_TABLE()

norqualizerFrame::norqualizerFrame(wxWindow* parent,wxWindowID id)
{
    //(*Initialize(norqualizerFrame)
    wxMenuItem* MenuItem2;
    wxMenuItem* MenuItem1;
    wxMenu* Menu1;
    wxMenuBar* MenuBar1;
    wxMenu* Menu2;

    Create(parent, id, _("NorQualizer 1.70"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("id"));
    SetClientSize(wxSize(731,481));
    {
    	wxIcon FrameIcon;
    	FrameIcon.CopyFromBitmap(wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_CDROM")),wxART_CMN_DIALOG));
    	SetIcon(FrameIcon);
    }
    StaticText1 = new wxStaticText(this, ID_STATICTEXT1, _("NorQualizer V1.70 - V.Portet 2018"), wxPoint(184,40), wxDefaultSize, 0, _T("ID_STATICTEXT1"));
    wxFont StaticText1Font(16,wxSWISS,wxFONTSTYLE_ITALIC,wxBOLD,false,wxEmptyString,wxFONTENCODING_DEFAULT);
    StaticText1->SetFont(StaticText1Font);
    Button1 = new wxButton(this, ID_BUTTON1, _("PROCESS FOLDER"), wxPoint(304,384), wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1"));
    Button2 = new wxButton(this, ID_BUTTON2, _("QUITT"), wxPoint(552,384), wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON2"));
    CheckBox1 = new wxCheckBox(this, ID_CHECKBOX1, _("The tracks have no bassline"), wxPoint(40,344), wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX1"));
    CheckBox1->SetValue(false);
    CheckBox2 = new wxCheckBox(this, ID_CHECKBOX2, _("Perform final MP3 encoding"), wxPoint(40,368), wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX2"));
    CheckBox2->SetValue(true);
    CheckBox3 = new wxCheckBox(this, ID_CHECKBOX3, _("Ignore short clicks (vinyle records)"), wxPoint(40,416), wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX3"));
    CheckBox3->SetValue(false);
    Button3 = new wxButton(this, ID_BUTTON3, _("PROCESS 1 FILE"), wxPoint(432,384), wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON3"));
    CheckBox4 = new wxCheckBox(this, ID_CHECKBOX4, _("Trace Events in a Log File"), wxPoint(40,320), wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX4"));
    CheckBox4->SetValue(false);
    Gauge1 = new wxGauge(this, ID_GAUGE1, 100, wxPoint(240,168), wxSize(224,16), 0, wxDefaultValidator, _T("ID_GAUGE1"));
    Gauge2 = new wxGauge(this, ID_GAUGE2, 100, wxPoint(240,224), wxSize(224,16), 0, wxDefaultValidator, _T("ID_GAUGE2"));
    StaticText2 = new wxStaticText(this, ID_STATICTEXT2, _("GLOBAL PROGRESSION"), wxPoint(248,208), wxDefaultSize, 0, _T("ID_STATICTEXT2"));
    StaticText3 = new wxStaticText(this, ID_STATICTEXT3, _("FILE IN PROCESS"), wxPoint(248,152), wxSize(368,13), 0, _T("ID_STATICTEXT3"));
    CheckBox5 = new wxCheckBox(this, ID_CHECKBOX5, _("Set MP3 rate to 192K (default 128k)"), wxPoint(40,392), wxSize(200,13), 0, wxDefaultValidator, _T("ID_CHECKBOX5"));
    CheckBox5->SetValue(false);
    StaticText4 = new wxStaticText(this, ID_STATICTEXT4, _("READY"), wxPoint(296,272), wxSize(104,22), 0, _T("ID_STATICTEXT4"));
    wxFont StaticText4Font(14,wxSWISS,wxFONTSTYLE_NORMAL,wxNORMAL,false,_T("Arial"),wxFONTENCODING_DEFAULT);
    StaticText4->SetFont(StaticText4Font);
    MenuBar1 = new wxMenuBar();
    Menu1 = new wxMenu();
    MenuItem1 = new wxMenuItem(Menu1, idMenuQuit, _("Quit\tAlt-F4"), _("Quit the application"), wxITEM_NORMAL);
    Menu1->Append(MenuItem1);
    MenuBar1->Append(Menu1, _("&File"));
    Menu2 = new wxMenu();
    MenuItem2 = new wxMenuItem(Menu2, idMenuAbout, _("About\tF1"), _("Show info about this application"), wxITEM_NORMAL);
    Menu2->Append(MenuItem2);
    MenuBar1->Append(Menu2, _("Help"));
    SetMenuBar(MenuBar1);
    StatusBar1 = new wxStatusBar(this, ID_STATUSBAR1, 0, _T("ID_STATUSBAR1"));
    int __wxStatusBarWidths_1[1] = { -1 };
    int __wxStatusBarStyles_1[1] = { wxSB_NORMAL };
    StatusBar1->SetFieldsCount(1,__wxStatusBarWidths_1);
    StatusBar1->SetStatusStyles(1,__wxStatusBarStyles_1);
    SetStatusBar(StatusBar1);
    DirDialog1 = new wxDirDialog(this, _("Select Music Folder..."), wxEmptyString, wxDD_DEFAULT_STYLE, wxDefaultPosition, wxDefaultSize, _T("wxDirDialog"));
    FileDialog1 = new wxFileDialog(this, _("Select Source Music File"), wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_DEFAULT_STYLE, wxDefaultPosition, wxDefaultSize, _T("wxFileDialog"));
    Timer1.SetOwner(this, ID_TIMER1);

    Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&norqualizerFrame::OnButton1Click);
    Connect(ID_BUTTON2,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&norqualizerFrame::OnButton2Click);
    Connect(ID_BUTTON3,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&norqualizerFrame::OnButton3Click);
    Connect(idMenuQuit,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&norqualizerFrame::OnQuit);
    Connect(idMenuAbout,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&norqualizerFrame::OnAbout);
    Connect(ID_TIMER1,wxEVT_TIMER,(wxObjectEventFunction)&norqualizerFrame::OnTimer1Trigger);
    //*)
}

norqualizerFrame::~norqualizerFrame()
{
    //(*Destroy(norqualizerFrame)
    //*)
}

void norqualizerFrame::OnQuit(wxCommandEvent& event)
{
    if(pending==0)
    {
        if(old_reserved!=0)
        {
            free(sample);
        }
        Close();
    }
}

void norqualizerFrame::OnAbout(wxCommandEvent& event)
{
    wxString msg = wxbuildinfo(long_f);
    wxMessageBox(msg, _("NorQualizer V1.70 - 2018 - V.Portet"));
}

void norqualizerFrame::OnButton1Click(wxCommandEvent& event)
{
    // Start folder selection...
    if (DirDialog1->ShowModal()==wxID_OK)
    {
        path=DirDialog1->GetPath();
        if(launch(MULTIPLE,path)!=0)
        {
            Beep(750,300);
        }
    }
}

void norqualizerFrame::OnButton3Click(wxCommandEvent& event)
{
    // Start single file selection...
    if (FileDialog1->ShowModal()==wxID_OK)
    {
        path=FileDialog1->GetPath();
        if(launch(SINGLE,path)!=0)
        {
            Beep(750,300);
        }
    }
}

int norqualizerFrame::launch(bool smode,wxString selpath)
{
    tracelog=false;
    if(CheckBox4->GetValue()==true)
    {
        tracelog=true;
        logfile=fopen("norqualizer-log.txt","w");
        if(logfile)
        {
            fprintf(logfile,"NorQualizer V1.70 - PROCESS LOG FILE\n");
            fprintf(logfile,"------------------------------------\nMODE: ");
            if(smode==SINGLE)
            {
                fprintf(logfile,"SINGLE FILE PROCESS\n\n");
            }
            else
            {
                fprintf(logfile,"MULTIPLE FILE (FOLDER) PROCESS\n\n");
            }
            fprintf(logfile,"SOURCE PATH: %s\n\n",selpath.c_str());
        }
        else
        {
            tracelog=false;
        }
    }

    if(CheckBox2->GetValue()==true)
    {
        makemp3out=1;
    }
    else
    {
        makemp3out=0;
    }

    // Reserve space for source path buffer
    lename=strlen(selpath.c_str());
    destpath=(char *)malloc(lename+20);
    if(destpath==NULL)
    {
        return 1;
    }

    // Launch single or multiple calls of norqualizer...
    if(smode==MULTIPLE)
    {
        wxDir::GetAllFiles(selpath,&filenames,wxEmptyString,wxDIR_FILES);
        numfiles=filenames.GetCount();
        if(numfiles==0)
        {
            return 1;
        }
        else
        {
            // Prepare destination path and create destination folder:
            // 1st create a sub folder in "path"
            strcpy(destpath,selpath.c_str());
            n=strlen(destpath);
            strcpy(&destpath[n],"\\norqualized");
            mkdir(destpath);
            // 2nd prepare path to this destination folder (Add "\" at the end)
            n=strlen(destpath);
            destpath[n++]='\\';
            destpath[n]=0;
            // 3rd Laucnh
            pending=true;
            atleastone=false;
            atleastoneok=false;
            filindex=0;
            Timer1.Start(150,false);
            Gauge1->SetValue(0);
            StaticText4->SetLabel(_("RUNNING"));
            return 0;
        }
    }
    else
    {
        // 1st extract the path only
        strcpy(destpath,selpath.c_str());
        n=strlen(destpath);
        while((n!=0)&&(destpath[n]!='\\'))
        {
            n--;
        }
        // 2nd prepare sub-folder path
        strcpy(&destpath[n],"\\norqualized");
        // 3rd create sub-folder
        mkdir(destpath);
        // 4th prepare path to this destination folder (Add "\" at the end)
        n=strlen(destpath);
        destpath[n++]='\\';
        destpath[n]=0;

        // Initiate full path source and launch
        numfiles=1;
        filenames.Add(selpath,1);
        pending=true;
        atleastone=false;
        atleastoneok=false;
        filindex=0;
        Timer1.Start(150,false);
        Gauge1->SetValue(0);
        StaticText4->SetLabel(_("RUNNING"));
        return 0;
    }
}


void norqualizerFrame::OnTimer1Trigger(wxTimerEvent& event)
{
    Timer1.Stop();
    char extens[4];
    unsigned int extind;
    unsigned int ffmpegexit;
    DWORD exitCode;
    if(pending==true)
    {
        Gauge1->SetValue(0);
        Refresh(true,NULL);
        Update();
        Sleep(30);
        if(tracelog==true)
        {
            fprintf(logfile,"Parsing %s...\n",filenames[filindex].c_str());
        }
        // Handle norqualization of files until the last one is reached
        // (If single file: first = last)
        lename=strlen(filenames[filindex].c_str());
        if(lename>=4)
        {
            namebuf=(char *)malloc(lename);
            argstring=(char *)malloc((2*lename)+114);
            if((namebuf!=NULL)&&(argstring!=NULL))
            {
                strcpy(namebuf,filenames[filindex].c_str());
                for(extind=0;extind!=3;extind++)
                {
                    extens[extind]=namebuf[extind+lename-3];
                }
                extens[3]=0;
                if(tracelog==true)
                {
                    fprintf(logfile,"Type: %s - ",strlwr(extens));
                }
                if((strcmp(strlwr(extens),"mp3")==0)||(strcmp(strlwr(extens),"mp4")==0)||(strcmp(strlwr(extens),"avi")==0)||(strcmp(strlwr(extens),"flv")==0)
                    ||(strcmp(strlwr(extens),"ogg")==0)||(strcmp(strlwr(extens),"wav")==0)||(strcmp(strlwr(extens),"wma")==0)||(strcmp(strlwr(extens),"wmv")==0)
                    ||(strcmp(strlwr(extens),"mkv")==0)||(strcmp(strlwr(extens),"mpg")==0))
                {
                    if(lename<45)
                    {
                        fordisplay.sprintf("%s",namebuf);
                    }
                    else
                    {
                        fordisplay.sprintf("...%s",&namebuf[lename-45]);
                    }
                    StaticText3->SetLabel(_(fordisplay));
                    atleastone=true;

                    if(strcmp(strlwr(extens),"wav")!=0)
                    {
                        // Show that something starts on progress bar
                        Gauge1->SetValue(5);
                        Refresh(true,NULL);
                        Update();
                        Sleep(100);
                        if(tracelog==true)
                        {
                            fprintf(logfile,"Step 1: Multimedia file to be pre-processed by ffmpeg...\n");
                        }
                        // Pre-extraction with FFMPEG
                        // 1st - Format the argument string buffer for FFMPEG
                        strcpy(&argstring[0],"ffmpeg -i \"");
                        strcpy(&argstring[11],namebuf);                               // Input file with path
                        strcpy(&argstring[11+lename],"\" -f wav -flags bitexact -acodec pcm_s16le -ar 44100 -ac 2 \"");  // Conversion parameters
                        strcpy(&argstring[71+lename],destpath);                       // Output file with path...
                        strcpy(&argstring[71+n+lename],"TMPOUT.WAV\"");
                        if(tracelog==true)
                        {
                            fprintf(logfile,"%s\n",argstring);
                        }

                        // Launch FFMPEG
                        if (CreateProcess(NULL,LPSTR(argstring),NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,NULL, NULL, &info, &processInfo))
                        {
                            ::WaitForSingleObject(processInfo.hProcess, INFINITE);
                            ffmpegexit=GetExitCodeProcess(processInfo.hProcess, &exitCode);
                            CloseHandle(processInfo.hProcess);
                            CloseHandle(processInfo.hThread);
                        }
                        else
                        {
                            ffmpegexit=0;
                        }

                        if(ffmpegexit!=0)
                        {
                            if(tracelog==true)
                            {
                                fprintf(logfile,"Audio extraction / conversion successful\n");
                            }
                            // Open TMPOUT.WAV as input for norqualizer
                            strcpy(argstring,destpath);
                            strcpy(&argstring[n],"TMPOUT.WAV");
                            wavopen=fopen(argstring,"rb");
                            if(wavopen)
                            {
                                fseek(wavopen,0,SEEK_END);
                                sourcel=ftell(wavopen);
                                sourcel-=44;
                                fseek(wavopen,0,SEEK_SET);
                                fread(&header[0],44,1,wavopen);
                                xmag=1;     // WAV file assumed OK, no resampling is needed.
                            }
                            else
                            {
                                fprintf(logfile,"ERROR: Temporary file TMPOUT.WAV was deleted by another process !\n\n");
                                ffmpegexit=0;
                            }
                        }
                        else
                        {
                            // Extraction via ffmpeg failed !
                            if(tracelog==true)
                            {
                                fprintf(logfile,"Audio extraction failed! This file will not be processed.\n\n");
                            }
                        }
                    }
                    else
                    {
                        if(tracelog==true)
                        {
                            fprintf(logfile,"Step 1: WAV file to be directly processed...  ");
                        }
                        // Directly load the WAV file
                        wavopen=fopen(namebuf,"rb");
                        if(wavopen)
                        {
                            ffmpegexit=1;       // Preset success flag (might be cleared in case of errors)
                   			fseek(wavopen,0,SEEK_END);
                            sourcel=ftell(wavopen);
                            if (sourcel>1024)
                            {
                                if(tracelog==true)
                                {
                                    fprintf(logfile,"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);
                                    if(tracelog==true)
                                    {
                                        fprintf(logfile,"This is not a valid WAV file at all !\n");
                                        ffmpegexit=0;
                                    }
                                }
                                else
                                {
                                    // Compute the expansion /shrinling ratio
                                    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);
                                            if(tracelog==true)
                                            {
                                                fprintf(logfile,"WAV header data seem to be corrupted. Try old command utility by using the -f option to load as raw data.\n");
                                            }
                                            ffmpegexit=0;
                                        }
                                    }
                                    // Finally, the result: Shrinking / expanding rate
                                    xmag = doubl_targetrate / ( ((float) sourcerate) * ( 3 * (nchannel - 1) + 1 ) );
                                }
                            }
                            else
                            {
                                if(tracelog==true)
                                {
                                    fprintf(logfile,"This WAV file is too short! It will be ignored !\n");
                                    ffmpegexit=0;
                                }
                                fclose(wavopen);
                            }
                        }
                        else
                        {
                            if(tracelog==true)
                            {
                                fprintf(logfile,"ERROR: Could not open this file !\n");
                            }
                        ffmpegexit=0;
                        }
                    }

                    if(ffmpegexit!=0)
                    {
                        Gauge1->SetValue(25);
                        Refresh(true,NULL);
                        Update();
                        Sleep(100);
                        atleastoneok=true;

                        // Call Norqualizer process
                        norqualizing();

                        // Post-encoding to MP3
                        if(makemp3out==1)
                        {
                            // Prepare command line for mp3 encoding
                            extind=strlen(argstring);                            // argstring contains the name+path of wav norqualized file
                            for(strparser=extind+11;strparser!=10;strparser--)
                            {
                                argstring[strparser]=argstring[strparser-11];    // Pre-shift to leave place at the beginning
                            }
                            argstring[0]='F';                                    // Insert preliminary command + parameters
                            argstring[1]='F';
                            argstring[2]='M';
                            argstring[3]='P';
                            argstring[4]='E';
                            argstring[5]='G';
                            argstring[6]=' ';
                            argstring[7]='-';
                            argstring[8]='i';
                            argstring[9]=' ';
                            argstring[10]='\"';
                            strcpy(&argstring[extind+11],"\" -f mp3 -ab 128k \"");     // Insert intermediate parameters
                            strncpy(&argstring[extind+30],&argstring[11],(extind-7));  // Repeat file+path string minus string "_eq.wav"
                            strcpy(&argstring[(2*extind)+23],".mp3\"");
                            if(CheckBox5->GetValue()==true)
                            {
                                // Replace bitrate string by "192K" instead of "128K"
                                argstring[extind+25]='9';
                                argstring[extind+26]='2';
                            }
                            if(tracelog==true)
                            {
                                fprintf(logfile,"MP3 re-encoding was requested... ");
                            }
                            Gauge1->SetValue(95);
                            Refresh(true,NULL);
                            Update();
                            Sleep(100);
                            // Launch FFMPEG
                            if (CreateProcess(NULL,LPSTR(argstring),NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,NULL, NULL, &info, &processInfo))
                            {
                                ::WaitForSingleObject(processInfo.hProcess, INFINITE);
                                ffmpegexit=GetExitCodeProcess(processInfo.hProcess, &exitCode);
                                CloseHandle(processInfo.hProcess);
                                CloseHandle(processInfo.hThread);
                                if(tracelog==true)
                                {
                                    fprintf(logfile,"OK\n\n");
                                }
                            }
                            else
                            {
                                if(tracelog==true)
                                {
                                    fprintf(logfile,"Failed ! This should not happen...\n\n");
                                }
                            }
                        }

                        // Erase TMPOUT.WAV if exists
                        strcpy(&argstring[0],destpath);
                        strcpy(&argstring[strlen(destpath)],"TMPOUT.WAV");
                        if((strcmp(strlwr(extens),"wav")!=0)&&(DeleteFile(LPSTR(argstring))==0))
                        {
                            if(tracelog==true)
                            {
                                fprintf(logfile,"Unable to delete %s !\n",argstring);
                                if(GetLastError()==ERROR_ACCESS_DENIED)
                                {
                                    fprintf(logfile,"Cause: Access Denied\n\n");
                                }
                                else
                                {
                                    fprintf(logfile,"Cause: Not present\n\n");
                                }
                            }
                        }
                    }
                }
                else
                {
                    if(tracelog==true)
                    {
                        fprintf(logfile,"Not a multimedia file - Ignored\n\n");
                    }
                }
                // Complete - Free memory
                free(namebuf);
                free(argstring);
            }
        }

        // Update display...
        filindex++;
        if(filindex==numfiles)
        {
            // Last one => Completed
            free(destpath);
            Gauge2->SetValue(100);
            Gauge1->SetValue(100);
            pending=0;
            // Timer1.Stop(); why?
            filenames.Clear();
            if(tracelog==true)
            {
                if(atleastone==false)
                {
                    fprintf(logfile,"\nERROR: No multimedia file in this folder !\n");
                }
                else
                {
                    if(atleastoneok==false)
                    {
                        fprintf(logfile,"\nERROR: File extractions could not be processed ! Check presence of FFMPEG.EXE !\n");
                    }
                }
                fclose(logfile);
            }
            if(atleastoneok==true)
            {
                StaticText4->SetLabel(_("DONE !"));
            }
            else
            {
                StaticText4->SetLabel(_("READY"));
                Beep(750,300);
            }
        }
        else
        {
            Gauge2->SetValue((filindex*100)/numfiles);
            Timer1.Start(150,false);
        }
    }
}

void norqualizerFrame::norqualizing(void)
{
    // Norqualizing process
    int dumm;
    unsigned char lowqsound=0;
    unsigned char popdiscri=0;
    unsigned char rattrap;
    unsigned char rattrapmax;
    unsigned long workvar;
    signed long workvar2;
    unsigned long reserved;
    unsigned long sourcepointer;
    unsigned long targetpointer;
    unsigned long pointopoint;
    unsigned long pointstep;
    // Click and pop discrimination
    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;
    // norqualization
    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=DEFAULT_REQUIREDLB;
    float requiredmb=13000;
    float requiredhb=4500;

    float tmpflt;

    unsigned char passcounter;
    unsigned char passnumber;

	// Does this WAV file have to be resampled ?
	// Deducing target length
	targetl = (((unsigned long) (xmag * sourcel))>>2)<<2;
	// Reserved memory
	if (targetl>sourcel)
	{
		reserved = 3*targetl;
	}
	else
	{
		reserved = (2*targetl) + sourcel;
	}

	if(old_reserved==0)
	{
        sample = (char *)malloc(reserved);
	}
	else
	{
	    if(reserved>old_reserved)
	    {
	        sample = (char *)realloc(sample,reserved);
	    }
	}

	// Pass ?
	if (sample == NULL)
	{
	    if(tracelog==true)
	    {
            fprintf(logfile,"Error: Not enough allocated memory !\n");
			fprintf(logfile,"source length = %ld - target length = %ld - required space = %ld\n",sourcel,targetl,reserved);
	    }
	    reserved=old_reserved;
		fclose(wavopen);
		return;
	}
	else
	{
	    old_reserved=reserved;
	}

    // Load norqualization parameters from GUI
    if(CheckBox1->GetValue()==true)
    {
        requiredlb=9000;
    }

    if(CheckBox3->GetValue()==true)
    {
        popdiscri=1;
    }

	// Need to resample ?
	if ((xmag>0.95)&&(xmag<1.05))
	{
		// No => Do a simple copy in the buffer...
		fread(&sample[0],1,sourcel,wavopen);
		fclose(wavopen);
		targetl=sourcel;
        kill_metadata();    // Clean loaded audio content at its beginning
	}
	else
	{
		// Load and resample file
		if(tracelog==true)
		{
            fprintf(logfile,"Step 1: File resampling...  ");
		}
		fread(&sample[0],sourcel,1,wavopen);
		fclose(wavopen);
        kill_metadata();    // Clean loaded audio content at its beginning
		// 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
			if(tracelog==true)
			{
                fprintf(logfile,"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
			if(tracelog==true)
			{
			    fprintf(logfile,"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;
			}
		}
	}

	// 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)&&(tracelog==true))
		{
			fprintf(logfile,"\nStep 1: Restarting from previously processed track in first pass...\n\n");
		}
        if(tracelog==true)
        {
            fprintf(logfile,"\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)
			{
                Gauge1->SetValue((int)25 + (int)(targetpointer/(targetl/70)));
                Refresh(true,NULL);
                Update();
                Sleep(30);
			}
            // *** 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--;
				}
			}
		}
        if(tracelog==true)
        {
            fprintf(logfile,"Monitored quasi-peaks:\n");
            fprintf(logfile,"LF: R = %d %d -- L = %d %d\n",quasipkminlowright,quasipkmaxlowright,quasipkminlowleft,quasipkmaxlowleft);
            fprintf(logfile,"MF: R = %d %d -- L = %d %d\n",quasipkminmidright,quasipkmaxmidright,quasipkminmidleft,quasipkmaxmidleft);
            fprintf(logfile,"HF: R = %d %d -- L = %d %d\n\n",quasipkminhighright,quasipkmaxhighright,quasipkminhighleft,quasipkmaxhighleft);
            fprintf(logfile,"Monitored peaks:\n");
            fprintf(logfile,"LF: R = %d %d -- L = %d %d\n",pkminlowright,pkmaxlowright,pkminlowleft,pkmaxlowleft);
            fprintf(logfile,"MF: R = %d %d -- L = %d %d\n",pkminmidright,pkmaxmidright,pkminmidleft,pkmaxmidleft);
            fprintf(logfile,"HF: R = %d %d -- L = %d %d\n\n",pkminhighright,pkmaxhighright,pkminhighleft,pkmaxhighleft);
            fprintf(logfile,"Step 3: Setting correction parameters...\n");
        }

        // *** Define normalization for each band ***

		// 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;
		}
        if(tracelog==true)
        {
            fprintf(logfile,"Before max peak considerations:\n");
            fprintf(logfile,"Left channel low freq: %f\n",leftampratelow);
            fprintf(logfile,"Right channel low freq: %f\n",rightampratelow);
            fprintf(logfile,"Left channel middle freq: %f\n",leftampratemid);
            fprintf(logfile,"Right channel middle freq: %f\n",rightampratemid);
            fprintf(logfile,"Left channel high freq: %f\n",leftampratehigh);
            fprintf(logfile,"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));
			}
		}
        if(tracelog==true)
        {
            fprintf(logfile,"After max peak considerations");
            if (popdiscri==0)
			{
                fprintf(logfile,":\n");
			}
            else
            {
                fprintf(logfile," (Clicks and pops not taken into account):\n");
            }
            fprintf(logfile,"Left channel low freq: %f\n",leftampratelow);
            fprintf(logfile,"Right channel low freq: %f\n",rightampratelow);
            fprintf(logfile,"Left channel middle freq: %f\n",leftampratemid);
            fprintf(logfile,"Right channel middle freq: %f\n",rightampratemid);
            fprintf(logfile,"Left channel high freq: %f\n",leftampratehigh);
            fprintf(logfile,"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)&&(tracelog==true))
			{
				fprintf(logfile,"Note: A double pass process will be necessary.\n\n");
			}
        }

		// Normalize and mix to final audio track
		// "Norqualization" now
		if(tracelog==true)
        {
            fprintf(logfile,"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
	if(tracelog==true)
	{
        fprintf(logfile,"Step 5: Saving result: \n");
	}
	// 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
	// Deducing save path from destpath and namebuf (original name extraction)
	dumm=strlen(namebuf);
	while((dumm!=0)&&(namebuf[dumm]!='\\'))
	{
	    dumm--;
	}
	if(dumm!=0)
	{
        dumm++;
	}
	// Now "dumm" points at the begining of file name after last backslash in "namebuf"
	strcpy(argstring,destpath);
	strcpy(&argstring[strlen(destpath)],&namebuf[dumm]);
	// Finally: update extension
	dumm = strlen(argstring);
	strcpy(&argstring[dumm-4],"_eq.wav");

	// Writing file
	wavopen=fopen(argstring,"wb");
	if (wavopen)
	{
		fwrite(&header[0],44,1,wavopen);
		fwrite(&sample[0],targetl,1,wavopen);
		fclose(wavopen);
		if(tracelog==true)
		{
            fprintf(logfile,"WAV Saved as %s\n\n",argstring);
		}
	}
	else
	{
		if(tracelog==true)
		{
            fprintf(logfile,"Could not write %s !\n\n",argstring);
            makemp3out=0;
		}
	}
}

void norqualizerFrame::kill_metadata(void)
{
    unsigned long metaprs;
    unsigned long metaprs2;
    t_splfull tmp16s;
    float tmpflt;
	if ((header[36]!='d')||(header[37]!='a')||(header[38]!='t')||(header[39]!='a'))
    {
        if(tracelog==true)
        {
            fprintf(logfile,"Skipping WAV metadata...\n");
        }
		metaprs = 0;
		while ((metaprs < 2048)&&(metaprs < (sourcel-8))&&((sample[metaprs]!='d')||(sample[metaprs+1]!='a')||(sample[metaprs+2]!='t')||(sample[metaprs+3]!='a')))
        {
			metaprs++;
        }
		if ((metaprs==2048)||(metaprs==(sourcel-8)))
        {
            if(tracelog==true)
            {
                fprintf(logfile, "(Warning: 'data' tag not found => Silencing the first %ld-byte chunk by default)\n",metaprs);
            }
        }
		// Whenever the "data" section tag is found or not, we silence the parsed field
		metaprs += 8;
		for (metaprs2=0; metaprs2 < metaprs; metaprs2+=4)
        {
            sample[metaprs2]=0;
            sample[metaprs2+1]=0;
            sample[metaprs2+2]=0;
            sample[metaprs2+3]=0;
        }
    }
	else
    {
		metaprs2 = 0;
    }
	// Now: Whenever some extra data had to be "silenced" or not, let's add a short fade-in anyway
	if ((sourcel - metaprs2) > 200)
    {
		for (unsigned long cfadin=metaprs2; cfadin < (metaprs2+200); cfadin+=2)
        {
			tmpflt = (float)(cfadin - metaprs2) / (float)200;
			tmp16s.via8b.lsb = sample[cfadin];
			tmp16s.via8b.msb = sample[cfadin+1];
			tmp16s.s16b = (signed int)(tmpflt * (float)tmp16s.s16b);
			sample[cfadin] = tmp16s.via8b.lsb;
			sample[cfadin+1] = tmp16s.via8b.msb;
        }
    }
}

void norqualizerFrame::OnButton2Click(wxCommandEvent& event)
{
    // Quit the program
    if(pending==false)
    {
        if(old_reserved!=0)
        {
            free(sample);
        }
        Close();
    }
}
