/* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.

iem_roomsim written by Thomas Musil (c) IEM KUG Graz Austria 2002 - 2018 */

#include "m_pd.h"
#include "iemlib.h"
#include "iem_roomsim.h"

/* -------------------------- block_lp1~ ------------------------------ */
static t_class *block_lp1_tilde_class;

typedef struct _block_lp1_tilde
{
	t_object	x_obj;
	t_sample	*x_begmem;
	int				x_blocksize;
  t_float   x_a;
  t_float   x_b;
	t_float		x_scalar_sig_in;
} t_block_lp1_tilde;

static void block_lp1_tilde_clear_mem(t_sample *vec, int n)
{
  int i;
  
  for(i=0; i<n; i++)
    vec[i] = 0.0f;
}

static void block_lp1_tilde_float(t_block_lp1_tilde *x, t_floatarg f)
{
  if(f < 0.0f)
    f = 0.0f;
  if(f > 1.0f)
    f = 1.0f;
  x->x_b = (t_float)f;
  x->x_a = 1.0f - x->x_b;
}

static void block_lp1_tilde_ft1(t_block_lp1_tilde *x, t_floatarg f)
{
  block_lp1_tilde_float(x, f);
}

static t_int *block_lp1_tilde_perform(t_int *w)
{ // for each freq-bin-index n and for each block-time-index m :  y(m,n) = b*y(m,n-1) + a*x(m,n)
	t_sample *in = (float *)(w[1]);
	t_sample *out = (float *)(w[2]);
	t_block_lp1_tilde *x = (t_block_lp1_tilde *)(w[3]);
	int i, n = (t_int)(w[4]);
	t_sample *old_block, fin=0.0f;
  t_float a=x->x_a, b=x->x_b;

	old_block = x->x_begmem;
	for(i=0; i<n; i++)
	{
		fin = in[i];
		out[i] = b*old_block[i] + a*fin;
		old_block[i] = out[i];
	}
	return(w+5);
}

static t_int *block_lp1_tilde_perf8(t_int *w)
{
	t_sample *in = (float *)(w[1]);
	t_sample *out = (float *)(w[2]);
	t_block_lp1_tilde *x = (t_block_lp1_tilde *)(w[3]);
	int i, n = (t_int)(w[4]);
  t_sample *old_block, fin[8];
  t_float a=x->x_a, b=x->x_b;

	old_block = x->x_begmem;
	while(n)
	{
		fin[0] = in[0];
		fin[1] = in[1];
		fin[2] = in[2];
		fin[3] = in[3];
		fin[4] = in[4];
		fin[5] = in[5];
		fin[6] = in[6];
		fin[7] = in[7];

    out[0] = b*old_block[0] + a*fin[0];
    out[1] = b*old_block[1] + a*fin[1];
    out[2] = b*old_block[2] + a*fin[2];
    out[3] = b*old_block[3] + a*fin[3];
    out[4] = b*old_block[4] + a*fin[4];
    out[5] = b*old_block[5] + a*fin[5];
    out[6] = b*old_block[6] + a*fin[6];
    out[7] = b*old_block[7] + a*fin[7];

		old_block[0] = out[0];
		old_block[1] = out[1];
		old_block[2] = out[2];
		old_block[3] = out[3];
		old_block[4] = out[4];
		old_block[5] = out[5];
		old_block[6] = out[6];
		old_block[7] = out[7];

		old_block += 8;
		in += 8;
		out += 8;
		n -= 8;
	}
	return(w+5);
}

static void block_lp1_tilde_dsp(t_block_lp1_tilde *x, t_signal **sp)
{
	int n = sp[0]->s_n;

	if(!x->x_blocksize)/*first time*/
  {
		x->x_begmem = (t_sample *)getbytes(n * sizeof(t_sample));
    block_lp1_tilde_clear_mem(x->x_begmem, n);
  }
	else if(x->x_blocksize != n)
  {
		x->x_begmem = (t_sample *)resizebytes(x->x_begmem, x->x_blocksize*sizeof(t_sample), n*sizeof(t_sample));
    block_lp1_tilde_clear_mem(x->x_begmem, n);
  }
	x->x_blocksize = n;
	if(n&7)
		dsp_add(block_lp1_tilde_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
	else
		dsp_add(block_lp1_tilde_perf8, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
}

static void *block_lp1_tilde_new(t_floatarg f)
{
	t_block_lp1_tilde *x = (t_block_lp1_tilde *)pd_new(block_lp1_tilde_class);

  block_lp1_tilde_float(x, f);
	x->x_blocksize = 0;
	x->x_begmem = (t_sample *)0;
	outlet_new(&x->x_obj, &s_signal);
	x->x_scalar_sig_in = (t_float)0.0;
  
  inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1"));
	return (x);
}

static void block_lp1_tilde_free(t_block_lp1_tilde *x)
{
	if(x->x_begmem)
		freebytes(x->x_begmem, x->x_blocksize * sizeof(t_sample));
}

void block_lp1_tilde_setup(void)
{
	block_lp1_tilde_class = class_new(gensym("block_lp1~"), (t_newmethod)block_lp1_tilde_new, (t_method)block_lp1_tilde_free,
		sizeof(t_block_lp1_tilde), 0, A_DEFFLOAT, 0);
	CLASS_MAINSIGNALIN(block_lp1_tilde_class, t_block_lp1_tilde, x_scalar_sig_in);
	class_addmethod(block_lp1_tilde_class, (t_method)block_lp1_tilde_dsp, gensym("dsp"), A_CANT, 0);
  class_addmethod(block_lp1_tilde_class, (t_method)block_lp1_tilde_ft1, gensym("ft1"), A_FLOAT, 0);
}
