/* 
  * Averaging of incoming frames (amp/freq-pairs)
  * uses recursive averaging (moving average)
  * this should be similar to the averaging done in csound's pvsblur, but more CPU friendly
  * vb, sept. 08
 */

/* argument sets maximal number of frames to average */
/* int sets current number of frames to average */

import com.cycling74.max.*;
import com.cycling74.msp.*;

public class vFrameAverage extends MSPPerformer
{
	
	private int		mdel = 1000;					// maximal number of frames 
	public int		kdel = 50;					// current frame delay
	private int		countr = 0;
	private int		vec = 512;					// signal vector size (spectral frame size)
	private double[]	accumA = new double[vec];			// amplitude accum
	private double[]	accumF = new double[vec];			// frequency accum
	private float[]	delayA = new float[vec*mdel];			// delay line for amplitudes
	private float[]	delayF = new float[vec*mdel];			// delay line for frequencies
	private boolean	reset_mem = false;
	
	
	private static final String[] INLET_ASSIST = new String[] {
		"input (sig): magnitude in", "input (sig): freq in"
	};
	private static final String[] OUTLET_ASSIST = new String[] {
		"output (sig): averaged magnitudes out", "output (sig): averaged frequencies out"
	};
	
	public vFrameAverage(Atom[] a) 
	 {
		declareInlets(new int[]{SIGNAL, SIGNAL});
		declareOutlets(new int[]{SIGNAL, SIGNAL});
		createInfoOutlet(false);
		
		setInletAssist(INLET_ASSIST);
		setOutletAssist(OUTLET_ASSIST);
		
		if(a.length > 0) {
			int arg = a[0].toInt();
			if(arg > 0) reset();
			else
				bail("(vFrameAverage) value out of range!\nargument sets max. number of frames to average.\n\tmust be an integer bigger than 0!\n");
		}
	}
	
	public void dspsetup(MSPSignal[] ins, MSPSignal[] outs) 
	 {
		vec = ins[0].vec.length;
		reset();
	}
	
	public void perform(MSPSignal[] ins, MSPSignal[] outs) 
	 {
		float[]	amps = ins[0].vec;		// magnitude input 
		float[]	freqs = ins[1].vec;		// frequency input
		float[]	outamp = outs[0].vec;
		float[]	outfreq = outs[1].vec;
		int i, j, start, delay_indx, avg_indx;
		float scale;
		
		
		if(reset_mem)
			reset();		// reset memory if delay time has changed
		
		if(countr >= mdel)
			countr = 0;
		start = countr - kdel;
		if(start < 0)
			start += mdel;
		
		scale = 1.f/kdel;
		delay_indx = countr*vec;
		avg_indx = start*vec;
		
		for(i=0; i<vec; i++) {
			delayA[delay_indx+i] = amps[i];
			delayF[delay_indx+i] = freqs[i];
			
			accumA[i] = accumA[i] + amps[i] - delayA[avg_indx+i];
			accumF[i] = accumF[i]  + freqs[i] - delayF[avg_indx+i];
			
			outamp[i] = (float)accumA[i] * scale;
			outfreq[i] = (float)accumF[i] * scale;
		}
		
		countr++;
	}
	
	public void reset() 
	 {
		delayA = new float[vec*mdel];
		delayF = new float[vec*mdel];
		accumA = new double[vec];
		accumF = new double[vec];
		
		reset_mem = false;
	}
	
	public void inlet(int d) 
	 {
		if(d>0 && d<mdel) {
			kdel = d;
			//reset();
			reset_mem = true;
		}
	}
}
