/*

FFTease - A set of Live Spectral Processors
Originally written by Eric Lyon and Christopher Penrose for the Max/MSP platform

Copyright (c)Thomas Grill (xovo@gmx.net)
For information on usage and redistribution, and for a DISCLAIMER OF ALL
WARRANTIES, see the file, "license.txt," in this distribution.  

*/

#include "main.h"


fftease::fftease(I mult,I flags):
	_inCount(0),
	_flags(flags),
	blsz(0),smprt(0),
	_mult(mult),_N(0)
{}

fftease::~fftease() {}

BL fftease::Init()
{
	Clear();
	Set();
	return flext_dsp::Init();
}

V fftease::Exit()
{
	Delete();
}

V fftease::m_dsp(I n,S *const *,S *const *)
{
	const F sr = Samplerate();
	if(n != blsz || sr != smprt) {
		blsz = n;
		smprt = sr;
		MakeVar();

		Delete();
		Set();
	}
}

V fftease::m_signal(I n,S *const *in,S *const *out)
{
	/* declare working variables */
	I i; 
	const I _N = get_N(),_Nw = _N,_N2 = _N/2; //,_Nw2 = _Nw/2; 

	/* fill our retaining buffers */
	_inCount += n;

	if(_flags&F_STEREO) {
		for(i = 0; i < _N-n ; i++ ) {
			_input1[i] = _input1[i+n];
			_input2[i] = _input2[i+n];
		}
		for(I j = 0; i < _N; i++,j++) {
			_input1[i] = in[0][j];
			_input2[i] = in[1][j];
		}
	}
	else {
		for (i = 0 ; i < _N-n ; i++ )
			_input1[i] = _input1[i+n];
		for (I j = 0; i < _N; i++,j++ )
			_input1[i] = in[0][j];
	}

	_rms = 0.;
	if(_flags&F_RMS) {
		for ( i=0; i < _Nw; i++ )
			_rms += _input1[i] * _input1[i];
		_rms = sqrt(_rms / _Nw);
	}

	/* apply hamming window and fold our window buffer into the fft buffer */
	fold( _input1, _Wanal, _Nw, _buffer1, _N, _inCount );
	if(_flags&F_STEREO) fold( _input2, _Wanal, _Nw, _buffer2, _N, _inCount );

	/* do an fft */
	if(_flags&F_BITSHUFFLE) {
		pv_rdft( _N, 1, _buffer1, _bitshuffle, _trigland );
		if(_flags&F_STEREO) pv_rdft( _N, 1, _buffer2, _bitshuffle, _trigland );
	}
	else {
		pv_rfft( _buffer1, _N2, 1);
		if(_flags&F_STEREO) pv_rfft( _buffer2, _N2,1);
	}

	if(!(_flags&F_NOSPEC)) {
		if(_flags&F_PHCONV) {
			convert( _buffer1, _channel1, _N2, _c_lastphase_in1, get_Fund(), _c_factor_in );
			if(_flags&F_STEREO) convert( _buffer2, _channel2, _N2, _c_lastphase_in2, get_Fund(), _c_factor_in );
		}
		else {
			leanconvert( _buffer1, _channel1, _N2 , !(_flags&F_NOAMP1),!(_flags&F_NOPH1));
			if(_flags&F_STEREO) leanconvert( _buffer2, _channel2, _N2 ,!(_flags&F_NOAMP2),!(_flags&F_NOPH2) );
		}
	}

	// ---- BEGIN --------------------------------

	Transform(_N,in+((_flags&F_STEREO)?2:1));

	// ---- END --------------------------------

	if(!(_flags&F_NOSPEC)) {
		if(_flags&F_PHCONV)
			unconvert( _channel1, _buffer1, _N2, _c_lastphase_out, get_Fund(), 1./_c_factor_in  );
		else
			leanunconvert( _channel1, _buffer1, _N2 );
	}


	/* do an inverse fft */
	if(_flags&F_BITSHUFFLE)
		pv_rdft( _N, -1, _buffer1, _bitshuffle, _trigland );
	else
		pv_rfft( _buffer1, _N2, 0);

	/* dewindow our result */
	overlapadd( _buffer1, _N, _Wsyn, _output, _Nw, _inCount);

	/* set our output and adjust our retaining output buffer */
	const F mult = 1./_N;
	for ( i = 0; i < n; i++ )
		out[0][i] = _output[i] * mult;

	for ( i = 0; i < _N-n; i++ )
		_output[i] = _output[i+n];
	for (; i < _N; i++ )
		_output[i] = 0.;
}


void fftease::Set()
{
	/* preset the objects data */
	const I n = Blocksize(),_N = n*Mult(),_Nw = _N,_N2 = _N/2; //,_Nw2 = _Nw/2;

	_inCount = -_Nw;

	/* assign memory to the buffers */
	_input1 = new F[_Nw];
	ZeroMem(_input1,_Nw*sizeof(*_input1));
	_buffer1 = new F[_N];
	if(_flags&F_STEREO) {
		_input2 = new F[_Nw];
		ZeroMem(_input2,_Nw*sizeof(*_input2));
		_buffer2 = new F[_N];
	}

	if(!(_flags&F_NOSPEC) || (_flags&F_SPECRES)) {
		_channel1 = new F[_N+2];
		ZeroMem(_channel1,(_N+2)*sizeof(*_channel1));
		if(_flags&F_STEREO) {
			_channel2 = new F[_N+2];
			ZeroMem(_channel2,(_N+2)*sizeof(*_channel2));
		}

		if(_flags&F_PHCONV) {
			_c_lastphase_in1 = new F[_N2+1];
			ZeroMem(_c_lastphase_in1,(_N2+1)*sizeof(*_c_lastphase_in1));
			if(_flags&F_STEREO) {
				_c_lastphase_in2 = new F[_N2+1];
				ZeroMem(_c_lastphase_in2,(_N2+1)*sizeof(*_c_lastphase_in2));
			}
			_c_lastphase_out = new F[_N2+1];
			ZeroMem(_c_lastphase_out,(_N2+1)*sizeof(*_c_lastphase_out));

			_c_factor_in = Samplerate()/(n * PV_2PI);
		}
	}

	_output = new F[_Nw];
	ZeroMem(_output,_Nw*sizeof(*_output));

	if(_flags&F_BITSHUFFLE) {
		_bitshuffle = new I[_N*2];
		_trigland = new F[_N*2];
		init_rdft( _N, _bitshuffle, _trigland);
	}

	_Hwin = new F[_Nw];
	_Wanal = new F[_Nw];
	_Wsyn = new F[_Nw];
	if(_flags&F_BALANCED)
		makewindows( _Hwin, _Wanal, _Wsyn, _Nw, _N, n, 0);
	else
		makehanning( _Hwin, _Wanal, _Wsyn, _Nw, _N, n, 0,0);
}

void fftease::Clear()
{
	_bitshuffle = NULL;
	_trigland = NULL;
	_input1 = _input2 = NULL;
	_Hwin = NULL;
	_Wanal = _Wsyn = NULL;
	_buffer1 = _buffer2 = NULL;
	_channel1 = _channel2 = NULL;
	_output = NULL;

	_c_lastphase_in1 = _c_lastphase_in2 = _c_lastphase_out = NULL;
}

void fftease::Delete()
{
	if(_input1) delete[] _input1;
	if(_buffer1) delete[] _buffer1;
	if(_channel1) delete[] _channel1;
	if(_input2) delete[] _input2;
	if(_buffer2) delete[] _buffer2;
	if(_channel2) delete[] _channel2;

	if(_c_lastphase_in1) delete[] _c_lastphase_in1;
	if(_c_lastphase_in2) delete[] _c_lastphase_in2;
	if(_c_lastphase_out) delete[] _c_lastphase_out;

	if(_output) delete[] _output;

	if(_bitshuffle) delete[] _bitshuffle;
	if(_trigland) delete[] _trigland;

	if(_Hwin) delete[] _Hwin;
	if(_Wanal) delete[] _Wanal;
	if(_Wsyn) delete[] _Wsyn;
}