/*-----------------------------------------------------------------------------

© 1999, Steinberg Soft und Hardware GmbH, All Rights Reserved

-----------------------------------------------------------------------------*/

#include <windows.h>
#include <stdio.h>
#include <math.h>
#include "iir.h"
#include "Vitalisierer.hpp"
#include "paramutils.h"

#define	MAX_FILTERTYPES		2
#define MAX_FILTERORDER		6
#define MIN_FILTERORDER		2
#define	MAX_FILTERQ			10

#define NUM_PARMS			7
#define NUM_FILTERMODE		3
#define MAX_FILTERMODE		(NUM_FILTERMODE-1)

#define ALPHA				0.4f
#define BETA				(1.0f-ALPHA)


char *pFTypeString[] =
{
	"Unknown",
	"Lowpass",
	"Highpass",
    "Bandpass",
	"Bandstop",
	"Peaking",
	"Low-Shelving",
	"High-Shelving"
};

enum eFilterMode
{
	eFilterModeBypass, eFilterModePre, eFilterModePost
};

char *gFilterModeStr[NUM_FILTERMODE] = 
{
	"Bypass", "Pre", "Post"
};

char *gSwitchStr[] = 
{
	"Off", "On"
};

enum eParams
{
	PARM_FILTER_TYPE, PARM_FILTER_ORDER, PARM_FILTERMODE, PARM_HIDHITUNE, PARM_HARMONICS, PARM_PROCESS, PARM_AUDITION
};

char *pParmStr[NUM_PARMS] = 
{
	"Filter-Type", "Order", "Filter-Mode", "Mid-Hi-Tune","Harmonics","Process","Audition"
};


//-----------------------------------------------------------------------------
Filter::Filter(audioMasterCallback audioMaster)
	: AudioEffectX(audioMaster, 1, NUM_PARMS)	// 1 program, 1 parameter only
{
	// Defaults
	m_fg = 4000.0f;					// Mid-Hi Tune
	m_filterType = IIR_FILTERTYPE_HIGHPASS;	
	m_filterMode = eFilterModePre;
	m_filterOrder = 2;
	m_filterQ = 1.0f;

	m_process = 0.2f;
	m_harmonics = 0.18f;
	m_audition = 0;
	
	// Initialze smoothed params
	m_smFg = m_fg;
	m_smProcess = m_process;
	m_smHarmonics = m_harmonics;
	m_smFilterQ = m_filterQ;

	m_nInputs = 2;
	m_nOutputs = 2;

	m_ppTemp = 0;

	setNumInputs(m_nInputs);		// stereo in
	setNumOutputs(m_nOutputs);		// stereo out
	setUniqueID('Vita');	// identify
	canMono();				// makes sense to feed both inputs with the same signal
	canProcessReplacing();	// supports both accumulating and replacing output
	strcpy(programName, "Default");	// default program name
	IIRInit(m_filterCoeff_L, MAX_FILTERORDER);
	IIRInit(m_filterCoeff_R, MAX_FILTERORDER);


//	IIRCalcFilterCoeff(m_filterCoeff_L, sampleRate, m_smFg*sampleRate, m_filterQ, m_filterOrder, IIR_FILTERTYPE_HIGHPASS);
//	IIRCalcFilterCoeff(m_filterCoeff_R, sampleRate, m_smFg*sampleRate, m_filterQ, m_filterOrder, IIR_FILTERTYPE_HIGHPASS);
}

//-----------------------------------------------------------------------------------------
Filter::~Filter()
{
	int i;
	sprintf(dbgStr, "Destruct = %d [0]",blockSize);
	if (m_ppTemp)
	{
		for(i=0; i < m_nOutputs; i++)
			delete [] m_ppTemp[i];

		delete [] m_ppTemp;
		m_ppTemp = 0;
		sprintf(dbgStr, "Destruct = %d [1]",blockSize);
	}
	OutputDebugString(dbgStr);
}

//-----------------------------------------------------------------------------------------
void Filter::setProgramName(char *name)
{
	strcpy(programName, name);
}

//-----------------------------------------------------------------------------------------
void Filter::getProgramName(char *name)
{
	strcpy(name, programName);
}

//-----------------------------------------------------------------------------------------
void Filter::setParameter(long index, float value)
{
	switch(index)
	{
	case PARM_FILTER_TYPE:
		m_paramFilterType = value;
		m_filterType = (long)norm2quant(1, MAX_FILTERTYPES, 1, m_paramFilterType);
		break;

	case PARM_FILTER_ORDER:
		m_paramFilterOrder = value;
		m_filterOrder = (long)norm2quant(MIN_FILTERORDER, MAX_FILTERORDER, 2, m_paramFilterOrder);
		break;
		
	case PARM_FILTERMODE:
		m_paramFilterMode = value;
		m_filterMode = (int)norm2quant(eFilterModeBypass, eFilterModePost, 1, value);
		break;

	case PARM_HIDHITUNE:
		m_paramFg = value;
		m_fg = 0.5f*m_paramFg*sampleRate;
		break;

	case PARM_HARMONICS:
		m_harmonics = value;
		break;

	case PARM_PROCESS:
		m_process = value;
		break;

	case PARM_AUDITION:
		m_audition = (int)norm2quant(0,1,1,value);
		break;
	
	
	}

}

//-----------------------------------------------------------------------------------------
float Filter::getParameter(long index)
{
	float value = 0;
	
	switch(index)
	{
	case PARM_FILTER_TYPE:
		m_paramFilterType = (float)quant2norm(1, MAX_FILTERTYPES, (float)m_filterType);
		value = m_paramFilterType;
		break;

	case PARM_FILTER_ORDER:
		m_paramFilterOrder = (float)quant2norm(2, MAX_FILTERORDER, (float)m_filterOrder);
		value = m_paramFilterOrder;
		break;
		
	case PARM_FILTERMODE:
		value = m_paramFilterMode = quant2norm(0,MAX_FILTERMODE,(float)m_filterMode);
		break;

	case PARM_HIDHITUNE:
		value = m_paramFg = 2.0f * m_fg /sampleRate;
		break;

	case PARM_HARMONICS:
		value = m_harmonics;
		break;

	case PARM_PROCESS:
		value = m_process;
		break;

	case PARM_AUDITION:
		value = (float)quant2norm(0,1,(float)m_audition);
		break;

	}
	return value;
}

//-----------------------------------------------------------------------------------------
void Filter::getParameterName(long index, char *label)
{
	strcpy(label,pParmStr[index]);
}

//-----------------------------------------------------------------------------------------
void Filter::getParameterDisplay(long index, char *text)
{
	switch(index)
	{
	case PARM_FILTER_TYPE:
		strcpy(text,pFTypeString[m_filterType]);
		break;

	case PARM_FILTER_ORDER:
		long2string(m_filterOrder, text);
		break;
		
	case PARM_FILTERMODE:
		strcpy(text,gFilterModeStr[m_filterMode]);
		break;

	case PARM_HIDHITUNE:
		float2string(m_fg,text);
		break;

	case PARM_HARMONICS:
		float2string(100.0f*m_harmonics,text);
		break;
	
	case PARM_PROCESS:
		float2string(100.0f*m_process,text);
		break;
	
	case PARM_AUDITION:
		strcpy(text,gSwitchStr[m_audition]);
		break;
	}
}

//-----------------------------------------------------------------------------------------
void Filter::getParameterLabel(long index, char *label)
{
	switch(index)
	{
	case PARM_FILTER_TYPE:
		strcpy(label, "           ");
		break;

	case PARM_FILTER_ORDER:
		strcpy(label, "           ");
		break;
		
	case PARM_FILTERMODE:
		strcpy(label, "           ");
		break;

	case PARM_HIDHITUNE:
		strcpy(label, "     Hz    ");
		break;

	case PARM_HARMONICS:
		strcpy(label, "     %     ");
		break;

	case PARM_PROCESS:
		strcpy(label, "     %     ");
		break;

	case PARM_AUDITION:
		strcpy(label, "           ");
		break;
	}
}

//-----------------------------------------------------------------------------------------
void Filter::process(float **inputs, float **outputs, long sampleFrames)
{
    float *in1  =  inputs[0];
    float *in2  =  inputs[1];
    float *out1 = outputs[0];
    float *out2 = outputs[1];

	while(--sampleFrames >= 0)
    {
        (*out1++) += (*in1++);    // accumulating
        (*out2++) += (*in2++);
    }
}

//-----------------------------------------------------------------------------------------
void Filter::processReplacing(float **inputs, float **outputs, long sampleFrames)
{
	float knorm, k, absX, X;
	int i;

    float *in1  =  inputs[0];
    float *in2  =  inputs[1];
    float *out1 = outputs[0];
    float *out2 = outputs[1];
	float *pDistorted1 = m_ppTemp[0];
	float *pDistorted2 = m_ppTemp[1];

	m_smFg = BETA*m_smFg+ALPHA*m_fg;
	m_smHarmonics = BETA*m_smHarmonics+ALPHA*m_harmonics;
	m_smProcess = BETA*m_smProcess+ALPHA*m_process;
	m_smFilterQ = BETA*m_smFilterQ+ALPHA*m_filterQ;

	k = 0.0001f + 10*m_smHarmonics;
	knorm = 1.0f/(1-expf(-k));

	IIRCalcFilterCoeff(m_filterCoeff_L, sampleRate, m_smFg, m_smFilterQ, m_filterOrder, m_filterType);
	IIRCalcFilterCoeff(m_filterCoeff_R, sampleRate, m_smFg, m_smFilterQ, m_filterOrder, m_filterType);

	if (sampleFrames > blockSize)
	{
		sprintf(dbgStr, "Scheisse");
		OutputDebugString(dbgStr);
		return;
	}

	// Pre-Filtering
	if (m_filterMode == eFilterModePre)
	{
		IIR(m_filterCoeff_L, in1, pDistorted1, m_filterOrder, sampleFrames);
		IIR(m_filterCoeff_R, in2, pDistorted2, m_filterOrder, sampleFrames);
	}
	else
	{
		memcpy(pDistorted1, in1, sampleFrames*sizeof(float));
		memcpy(pDistorted2, in2, sampleFrames*sizeof(float));
	}

	for (i=0; i < sampleFrames; i++)
	{
		X = pDistorted1[i];
		absX = fabsf(X);

		if (X > 0.0f)
			pDistorted1[i] =  knorm * (1 - expf(-k*absX)) - X;
		else
			pDistorted1[i] = -knorm * (1 - expf(-k*absX)) - X;
	}

	for (i=0; i < sampleFrames; i++)
	{
		X = pDistorted2[i];
		absX = fabsf(X);
		if (X > 0.0f)
			pDistorted2[i] =  knorm * (1 - expf(-k*absX)) - X;
		else
			pDistorted2[i] = -knorm * (1 - expf(-k*absX)) - X;
	}

	// Post-Filtering
	if (m_filterMode == eFilterModePost)
	{
		IIR(m_filterCoeff_L, pDistorted1, pDistorted1, m_filterOrder, sampleFrames);
		IIR(m_filterCoeff_R, pDistorted2, pDistorted2, m_filterOrder, sampleFrames);
	}

	// Mixing
	if (m_audition)
	{
		for (i=0; i < sampleFrames; i++)
		{
			out1[i] = m_smProcess * pDistorted1[i];
			out2[i] = m_smProcess * pDistorted2[i];
		}
	}
	else
	{
		for (i=0; i < sampleFrames; i++)
		{
			out1[i] = m_smProcess * pDistorted1[i] + in1[i];
			out2[i] = m_smProcess * pDistorted2[i] + in2[i];
		}
	}	
}

//-----------------------------------------------------------------------------------------
void Filter::setSampleRate(float sampleRate)
{
	this->sampleRate = sampleRate;
	sprintf(dbgStr, "Sample Rate = %f",sampleRate);
	OutputDebugString(dbgStr);
}

//-----------------------------------------------------------------------------------------
void Filter::setBlockSize(long blockSize)
{
	int i;
	this->blockSize = blockSize;
	sprintf(dbgStr, "Block Size = %d [0]",blockSize);
	if (m_ppTemp)
	{
		for(i=0; i < m_nOutputs; i++)
			delete [] m_ppTemp[i];

		delete [] m_ppTemp;
		m_ppTemp = 0;
		sprintf(dbgStr, "Block Size = %d [1]",blockSize);
	}
	m_ppTemp = new float*[m_nOutputs];
	sprintf(dbgStr, "Temp = 0x%X",m_ppTemp);
	OutputDebugString(dbgStr);

	for(i=0; i < m_nOutputs; i++)
		m_ppTemp[i] = new float[2*blockSize];
	OutputDebugString(dbgStr);

}
