/* 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_peak_env~ ------------------------------ */
static t_class *block_peak_env_tilde_class;

typedef struct _block_peak_env_tilde
{
	t_object	x_obj;
	t_sample	*x_begmem;
	int				x_blocksize;
  t_float   x_b;
	t_float		x_scalar_sig_in;
} t_block_peak_env_tilde;

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

static void block_peak_env_tilde_float(t_block_peak_env_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;
}

static void block_peak_env_tilde_ft1(t_block_peak_env_tilde *x, t_floatarg f)
{
  block_peak_env_tilde_float(x, f);
}

static t_int *block_peak_env_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_peak_env_tilde *x = (t_block_peak_env_tilde *)(w[3]);
	int i, n = (t_int)(w[4]);
	t_sample *old_block, fin=0.0f, gout=0.0f;
  t_float b=x->x_b;

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

static t_int *block_peak_env_tilde_perf8(t_int *w)
{
	t_sample *in = (float *)(w[1]);
	t_sample *out = (float *)(w[2]);
	t_block_peak_env_tilde *x = (t_block_peak_env_tilde *)(w[3]);
	int i, n = (t_int)(w[4]);
  t_sample *old_block, fin[8], gout[8];
  t_float 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];

    gout[0] = b*old_block[0];
    gout[1] = b*old_block[1];
    gout[2] = b*old_block[2];
    gout[3] = b*old_block[3];
    gout[4] = b*old_block[4];
    gout[5] = b*old_block[5];
    gout[6] = b*old_block[6];
    gout[7] = b*old_block[7];
    
    if(fin[0] >= gout[0])
      out[0] = fin[0];
    else
      out[0] = gout[0];
    if(fin[1] >= gout[1])
      out[1] = fin[1];
    else
      out[1] = gout[1];
    if(fin[2] >= gout[2])
      out[2] = fin[2];
    else
      out[2] = gout[2];
    if(fin[3] >= gout[3])
      out[3] = fin[3];
    else
      out[3] = gout[3];
    if(fin[4] >= gout[4])
      out[4] = fin[4];
    else
      out[4] = gout[4];
    if(fin[5] >= gout[5])
      out[5] = fin[5];
    else
      out[5] = gout[5];
    if(fin[6] >= gout[6])
      out[6] = fin[6];
    else
      out[6] = gout[6];
    if(fin[7] >= gout[7])
      out[7] = fin[7];
    else
      out[7] = gout[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_peak_env_tilde_dsp(t_block_peak_env_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_peak_env_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_peak_env_tilde_clear_mem(x->x_begmem, n);
  }
	x->x_blocksize = n;
	if(n&7)
		dsp_add(block_peak_env_tilde_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
	else
		dsp_add(block_peak_env_tilde_perf8, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
}

static void *block_peak_env_tilde_new(t_floatarg f)
{
	t_block_peak_env_tilde *x = (t_block_peak_env_tilde *)pd_new(block_peak_env_tilde_class);

  block_peak_env_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_peak_env_tilde_free(t_block_peak_env_tilde *x)
{
	if(x->x_begmem)
		freebytes(x->x_begmem, x->x_blocksize * sizeof(t_sample));
}

void block_peak_env_tilde_setup(void)
{
	block_peak_env_tilde_class = class_new(gensym("block_peak_env~"), (t_newmethod)block_peak_env_tilde_new, (t_method)block_peak_env_tilde_free,
		sizeof(t_block_peak_env_tilde), 0, A_DEFFLOAT, 0);
	CLASS_MAINSIGNALIN(block_peak_env_tilde_class, t_block_peak_env_tilde, x_scalar_sig_in);
	class_addmethod(block_peak_env_tilde_class, (t_method)block_peak_env_tilde_dsp, gensym("dsp"), A_CANT, 0);
  class_addmethod(block_peak_env_tilde_class, (t_method)block_peak_env_tilde_ft1, gensym("ft1"), A_FLOAT, 0);
}
