/* Copyright (c) 1997 The Regents of the University of California.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/*  The sig~ and line~ routines; possibly fancier envelope generators to
    come later.
*/

#include "m_pd.h"

/* -------------------------- sig~ ------------------------------ */
static t_class *sig_class;

typedef struct _sig
{
    t_object x_obj;
    float x_f;
} t_sig;

static t_int *sig_perform(t_int *w)
{
    t_float *in = (t_float *)(w[1]);
    t_float *out = (t_float *)(w[2]);
    int n = (int)(w[3]);
    while (n--) *out++ = *in; 
    return (w+4);
}

static void sig_float(t_sig *x, t_float f)
{
    x->x_f = f;
}

static void sig_dsp(t_sig *x, t_signal **sp)
{
    dsp_add(sig_perform, 3, &x->x_f, sp[0]->s_vec, sp[0]->s_n);
}

static void *sig_new(t_floatarg f)
{
    t_sig *x = (t_sig *)pd_new(sig_class);
    x->x_f = f;
    outlet_new(&x->x_obj, gensym("signal"));
    return (x);
}

static void sig_setup(void)
{
    sig_class = class_new(gensym("sig~"), (t_newmethod)sig_new, 0,
    	sizeof(t_sig), 0, A_DEFFLOAT, 0);
    class_addfloat(sig_class, (t_method)sig_float);
    class_addmethod(sig_class, sig_dsp, gensym("dsp"), 0);
}

/* -------------------------- line~ ------------------------------ */
static t_class *line_class;

typedef struct _line
{
    t_object x_obj;
    float x_target;
    float x_value;
    float x_biginc;
    float x_inc;
    float x_1overn;
    float x_msectodsptick;
    float x_inletvalue;
    float x_inletwas;
    int x_ticksleft;
    int x_retarget;
} t_line;

static t_int *line_perform(t_int *w)
{
    t_line *x = (t_line *)(w[1]);
    t_float *out = (t_float *)(w[2]);
    int n = (int)(w[3]);
    float f;
    if (x->x_retarget)
    {
    	int nticks = x->x_inletwas * x->x_msectodsptick;
    	if (!nticks) nticks = 1;
    	x->x_ticksleft = nticks;
    	x->x_biginc = (x->x_target - x->x_value)/(float)nticks;
    	x->x_inc = x->x_1overn * x->x_biginc;
    	x->x_retarget = 0;
    }
    if (x->x_ticksleft)
    {
    	float f = x->x_value;
    	while (n--) *out++ = f, f += x->x_inc;
    	x->x_value += x->x_biginc;
    	x->x_ticksleft--;
    }
    else
    {
    	x->x_value = x->x_target;
    	while (n--) *out++ = x->x_value;
    }
    return (w+4);
}

static void line_float(t_line *x, t_float f)
{
    if (x->x_inletvalue <= 0)
    {
    	x->x_target = x->x_value = f;
    	x->x_ticksleft = x->x_retarget = 0;
    }
    else
    {
    	x->x_target = f;
    	x->x_retarget = 1;
    	x->x_inletwas = x->x_inletvalue;
    	x->x_inletvalue = 0;
    }
}

static void line_dsp(t_line *x, t_signal **sp)
{
    dsp_add(line_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
    x->x_1overn = 1./sp[0]->s_n;
    x->x_msectodsptick = sp[0]->s_sr / (1000 * sp[0]->s_n);
}

static void *line_new()
{
    t_line *x = (t_line *)pd_new(line_class);
    outlet_new(&x->x_obj, gensym("signal"));
    floatinlet_new(&x->x_obj, &x->x_inletvalue);
    x->x_ticksleft = x->x_retarget = 0;
    x->x_value = x->x_target = x->x_inletvalue = x->x_inletwas = 0;
    return (x);
}

static void line_setup(void)
{
    line_class = class_new(gensym("line~"), line_new, 0,
    	sizeof(t_line), 0, 0);
    class_addfloat(line_class, (t_method)line_float);
    class_addmethod(line_class, line_dsp, gensym("dsp"), 0);
}

/* -------------------------- snapshot~ ------------------------------ */
static t_class *snapshot_class;

typedef struct _snapshot
{
    t_object x_obj;
    t_sample x_value;
} t_snapshot;

static t_int *snapshot_perform(t_int *w)
{
    t_float *in = (t_float *)(w[1]);
    t_float *out = (t_float *)(w[2]);
    *out = *in;
    return (w+3);
}

static void snapshot_dsp(t_snapshot *x, t_signal **sp)
{
    dsp_add(snapshot_perform, 2, sp[0]->s_vec + (sp[0]->s_n-1), &x->x_value);
}

static void snapshot_bang(t_snapshot *x)
{
    outlet_float(x->x_obj.ob_outlet, x->x_value);
}

static void *snapshot_new()
{
    t_snapshot *x = (t_snapshot *)pd_new(snapshot_class);
    x->x_value = 0;
    outlet_new(&x->x_obj, &s_float);
    return (x);
}

static void snapshot_setup(void)
{
    snapshot_class = class_new(gensym("snapshot~"), snapshot_new, 0,
    	sizeof(t_snapshot), 0, 0);
    class_addmethod(snapshot_class, nullfn, gensym("signal"), 0);
    class_addmethod(snapshot_class, snapshot_dsp, gensym("dsp"), 0);
    class_addbang(snapshot_class, snapshot_bang);
}

/* ------------------------ global setup routine ------------------------- */

void d_ctl_setup(void)
{
    sig_setup();
    line_setup();
    snapshot_setup();
}

