/* oscildiff~.c  one float inlet, three signal outlet
** compare wavetable with sine, truncation, round
** arguments: freq tablesize (max 4096)
** 02/11/04 IF
** 03/10/21 IF
*/

#include "ext.h"    // Required for all Max external objects
#include "z_dsp.h"  // Required for all MSP external objects
//#include <stdlib.h> // For exit(0)
void *oscil_class;

#define MAX_TABSIZE 4096
#define MIN_TABSIZE 4
#define DEFAULT_TABSIZE 512

typedef struct _oscil 	// Data structure for this object
{
    t_pxobject t_obj;
    t_float t_freq;
    t_float t_sr;
    t_float t_ptr;
    t_int   t_tabsize;
    t_float t_tab[MAX_TABSIZE + 1];
    void *t_out1;
} t_oscil;

void *oscil_new(float freq, float tabsize);
t_int *oscil_perform(t_int *w);
void oscil_dsp(t_oscil *x, t_signal **sp, short *count);
void oscil_float(t_oscil *x, t_float val);
void oscil_filltable(t_oscil *x);

int main(void)
{
    setup((t_messlist **)&oscil_class, (method)oscil_new, (method)dsp_free, 
    		(short)sizeof(t_oscil), 0L, A_DEFFLOAT, A_DEFFLOAT, 0);
    addmess((method)oscil_dsp, "dsp", A_CANT, 0);
    addfloat((method)oscil_float);		
    dsp_initclass();						// in floatin()
	return(0);
}

void *oscil_new(float freq, float tabsize)
{
	t_oscil *x = (t_oscil *)newobject(oscil_class);
	
	x->t_freq = freq;
	x->t_tabsize = DEFAULT_TABSIZE;

	if (freq == 0.)
		x->t_freq = 440;

	if (tabsize > MAX_TABSIZE)
		x->t_tabsize = MAX_TABSIZE;
	else if (tabsize < MIN_TABSIZE)
		x->t_tabsize = MIN_TABSIZE;
	else if (tabsize != 0.) 
	    x->t_tabsize = tabsize;

post("TABSIZE: %d %f",  x->t_tabsize, tabsize);
	x->t_ptr = 0;
	dsp_setup((t_pxobject *)x, 0);			// left signal inlet, must come after floatin
	x->t_out1 = floatout((t_pxobject *)x);
	outlet_new((t_pxobject *)x, "signal");	// sine out
	outlet_new((t_pxobject *)x, "signal");	// truncate
	outlet_new((t_pxobject *)x, "signal");	// round
	outlet_new((t_pxobject *)x, "signal");	// interpolate
	oscil_filltable(x); // fill table
	return (x);
}

void oscil_filltable(t_oscil *x)
{
	int i;
	for (i = 0; i < x->t_tabsize; i++)
		x->t_tab[i] = sin(2 * PI * i / (float)x->t_tabsize);
	x->t_tab[x->t_tabsize] = x->t_tab[0];
}

void oscil_float(t_oscil *x, t_float val)
{
	x->t_freq = val;
}
	
void oscil_dsp(t_oscil *x, t_signal **sp, short *count)
{
	x->t_sr = sp[0]->s_sr; // get sampling rate
post("Vector size: %d", sp[0]->s_n);
	dsp_add(oscil_perform, 6, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[0]->s_n, x);
}

t_int *oscil_perform(t_int *w)
{
	float p, p1, z;
    t_float *out0 = (t_float *)(w[1]);
    t_float *out1 = (t_float *)(w[2]);
    t_float *out2 = (t_float *)(w[3]);
    t_float *out3 = (t_float *)(w[4]);
	int n = (int)(w[5]);
	t_oscil *x = (t_oscil *)w[6];
	float incr = x->t_tabsize * x->t_freq / x->t_sr;
	float incr1 = 2 * PI * x->t_freq / x->t_sr;
	int ip;
	float trunc = 0;
	
	p = x->t_ptr; // starting phase
	p1 = p * 2 * PI / x->t_tabsize;
	
	while (n--) 
	{
		*out0++ = z = sin(p1);	
		trunc += *out1++ = z - x->t_tab[(int)p];
		*out2++ = z - x->t_tab[(int)(p + 0.5)];
		ip = (int)p;
		*out3++ = z - ((x->t_tab[ip + 1] - x->t_tab[ip]) * (p - ip) + x->t_tab[ip]);
		p += incr;
		if 	(p >= x->t_tabsize)
			p -= x->t_tabsize;
		p1 += incr1;
		if (p1 > 2 * PI)
			p1 -= 2 * PI;
	}
	x->t_ptr = p; // ending phase
	n = (int)(w[5]);
	trunc /= n;
//	outlet_float(x->t_out1, trunc);
    return (w + 7); // always add one more than the 2nd argument in dsp_add()
}


