/* 

VASP modular - vector assembling signal processor / objects for Max/MSP and PD

Copyright (c) 2002 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"
#include "ops_flt.h"
#include "opdefs.h"
#include "util.h"

// --- highpass ---------------------------------------

//! \todo handle carry
//! \todo handle yield

BL VecOp::d_fhp(OpParam &p) 
{ 
	if(p.revdir)
		post("%s - reversing operation direction due to overlap: opposite sample delay",p.opname);

/*
	R coef = (2*PI)/perln;
    if(coef > 1) coef = 1;
*/  
    const R coef = 1-p.flt.coef;
	const I arep = abs(p.flt.rep);
	S *src = p.rsdt,*dst = p.rddt;

	for(I ti = 0; ti < arep; ++ti) {
		register S v1;
		I i;

		// t+ direction
		for(i = 0,v1 = 0; i < p.frames; ++i) {
			register const S v0 = *src + coef*v1;
			*dst = v0-v1;
			v1 = v0;
			src += p.rss,dst += p.rds;
		}
		
		if(p.flt.rep < 0) {
			if(++ti == arep) break;

			// t- direction
			for(i = p.frames-1,v1 = 0; i >= 0; --i) {
				src -= p.rss,dst -= p.rds;
				register const S v0 = *src + coef*v1;
				*dst = v0-v1;
				v1 = v0;
			}
		}
	}

	return true;
}


// --- lowpass ---------------------------------------

//! \todo handle carry
//! \todo handle yield

BL VecOp::d_flp(OpParam &p) 
{ 
	if(p.revdir)
		post("%s - reversing operation direction due to overlap: opposite sample delay",p.opname);

/*
	R coef = (2*PI)/perln;
    if(coef > 1) coef = 1;
*/    
    
	const R coef = p.flt.coef,feed = 1-coef;
	const I arep = abs(p.flt.rep);

	for(I ti = 0; ti < arep; ++ti) {
		register S v1;
		I i;
		S *src = p.rsdt,*dst = p.rddt;

		// t+ direction
		for(i = 0,v1 = 0; i < p.frames; ++i) {
			v1 = *dst = coef* *src + feed*v1;
			src += p.rss,dst += p.rds;
		}
		
		if(p.flt.rep < 0) {
			if(++ti == arep) break;

			// t- direction
			for(i = p.frames-1,v1 = 0; i >= 0; --i) {
				src -= p.rss,dst -= p.rds;
				v1 = *dst = coef* *src + feed*v1;
			}
		}
	}

	return true;
}



Vasp *VaspOp::m_fhp(OpParam &p,CVasp &src,const Argument &arg,CVasp *dst,BL hp) 
{ 
	Vasp *ret = NULL;
	if(arg.IsList() && arg.GetList().Count() >= 1) {
		RVecBlock *vecs = GetRVecs(p.opname,src,dst);
		if(vecs) {
			p.flt.coef = 2*PI/flext::GetAFloat(arg.GetList()[0]);
		    if(p.flt.coef > 1) p.flt.coef = 1;
			p.flt.rep = arg.GetList().Count() >= 2?flext::GetAInt(arg.GetList()[1]):1;
			p.flt.rep = -p.flt.rep;  // fwd/bwd operation
/*
			if(p.SROvr()) {
				p.SDRRev();
				post("%s - reversing operation direction due to overlap: opposite sample delay",opnm);
			}	
*/
			ret = DoOp(vecs,hp?VecOp::d_fhp:VecOp::d_flp,p);

			delete vecs;
		}
	}

	return ret;
}

VASP_ANYOP("vasp.flp",flp,1,true,VASP_ARG(),"Passive low pass filter")
VASP_ANYOP("vasp.fhp",fhp,1,true,VASP_ARG(),"Passive high pass filter")


// --- integrate/differentiate

/*! \brief Integration
	\remark The delay of the result is +/- one sample, depending on the direction of the calculation 
	
	\todo different modes how to initialize first carry?
	\todo repetition count
*/
BL VecOp::d_int(OpParam &p) 
{ 
	if(p.revdir)
		post("%s - reversed operation direction due to overlap: opposite sample delay",p.opname);

	register S d = p.intdif.carry;
	register I i;
	_DE_LOOP(i,p.frames, ( *p.rddt = (d += *p.rsdt), p.rsdt += p.rss,p.rddt += p.rds ) )
	p.intdif.carry = d;
	return true; 
}

/*! \brief Differentiation
	\remark The delay of the result is +/- one sample, depending on the direction of the calculation 

	\todo different modes how to initialize first carry?
	\todo repetition count
*/
BL VecOp::d_dif(OpParam &p) 
{ 
	if(p.revdir)
		post("%s - reversed operation direction due to overlap: opposite sample delay",p.opname);

	register S d = p.intdif.carry,d1;
	register I i;
	_DE_LOOP(i,p.frames, ( d1 = *p.rsdt, *p.rddt = d1-d,d = d1, p.rsdt += p.rss,p.rddt += p.rds ) )
	p.intdif.carry = d;
	return true; 
}

/*! \brief Does vasp integration/differentiation.

	\param arg argument list 
	\param dst destination vasp (NULL for in-place operation)
	\param inv true for differentiation
	\return normalized destination vasp
*/
Vasp *VaspOp::m_int(OpParam &p,CVasp &src,const Argument &arg,CVasp *dst,BL inv) 
{ 
	Vasp *ret = NULL;
	RVecBlock *vecs = GetRVecs(p.opname,src,dst);
	if(vecs) {
		p.intdif.carry = 0,p.intdif.rep = 1;
		if(arg.IsList() && arg.GetList().Count() >= 1) p.intdif.rep = flext::GetAInt(arg.GetList()[0]);
		
		if(p.intdif.rep < 0) {
			post("%s - invalid repetition count (%i) -> set to 1",p.opname,p.intdif.rep);
			p.intdif.rep = 1;
		}
		
		ret = DoOp(vecs,inv?VecOp::d_dif:VecOp::d_int,p);
		delete vecs;
	}
	return ret;
}

VASP_ANYOP("vasp.int",int,0,true,VASP_ARG_I(1),"Integration") 
VASP_ANYOP("vasp.dif",dif,0,true,VASP_ARG_I(1),"Differentiation") 


VASP_UNARY("vasp.fix",fix,true,"Bashes denormals/NANs to zero")