/* oscili~.c  one float inlet, one signal outlet
** uses wavetable with interpolation
** 02/11/04 IF
** 04/10/28 IF Xcode
*/

#include "ext.h"    // Required for all Max external objects
#include "z_dsp.h"  // Required for all MSP external objects

void *oscil_class;

#define TABSIZE 512    // The manual says cycle~ has 512
#define DEF_FREQ 440

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_float t_tab[TABSIZE + 1];
} t_oscil;

void *oscil_new(void);
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, 0);
    addmess((method)oscil_dsp, "dsp", A_CANT, 0);
    addfloat((method)oscil_float);		
    dsp_initclass();						// in floatin()
	return(0);
}

void *oscil_new(void)
{
	t_oscil *x = (t_oscil *)newobject(oscil_class);
	x->t_freq = DEF_FREQ;
	x->t_ptr = 0;
	dsp_setup((t_pxobject *)x, 0);			// left signal inlet, must come after floatin
	outlet_new((t_pxobject *)x, "signal");	// left signal outlet, must come after floatout
	oscil_filltable(x);
	return (x);
}

void oscil_filltable(t_oscil *x)
{
	int i;
	for (i = 0; i < TABSIZE; i++)
		x->t_tab[i] = cos(TWOPI * i / TABSIZE);
	x->t_tab[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
	dsp_add(oscil_perform, 3, sp[0]->s_vec, sp[0]->s_n, x);
}

t_int *oscil_perform(t_int *w)
{
	float p;
	int ip;
    t_float *out = (t_float *)(w[1]);
	int n = (int)(w[2]);
	t_oscil *x = (t_oscil *)w[3];
	float incr = TABSIZE * x->t_freq / x->t_sr;
	
	p = x->t_ptr;
	while (n--) 
	{
		ip = (int)p;
		*out++ = (x->t_tab[ip + 1] - x->t_tab[ip]) * (p - ip) + x->t_tab[ip];
		p += incr;
		while (p > TABSIZE)
			p -= TABSIZE;
	}
	x->t_ptr = p;
		
    return (w + 4); // always add one more than the 2nd argument in dsp_add()
}


