/* oscilTime~.c  one float inlet, one signal outlet
** 
** 02/11/04 IF
** 04/10/26 IF 
*/

#include <math.h>
#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
#define DEF_FREQ 440
#define DEF_LOOP_SIZE 1000

typedef struct _oscil 	// Data structure for this object
{
    t_pxobject t_obj;
    t_float t_freq;  // current frequenct
    t_float t_sr;	// smapling rate
    t_float t_ptr;	// current phase
	t_int t_n;     // loop size
    void *t_outlet; // float out
    t_float t_tab[TABSIZE + 1];
    t_float *t_out;
} t_oscil;

void *oscil_new(long n);
t_int *oscil_perform(t_int *w);
void oscil_float(t_oscil *x, t_float val);
void oscil_bang(t_oscil *x);
void oscil_sin(t_oscil *x);
void oscil_interp(t_oscil *x);
void oscil_filltable(t_oscil *x);
void oscil_trunc(t_oscil *x);

int main(void)
{
    setup((t_messlist **)&oscil_class, (method)oscil_new, 0L, (short)sizeof(t_oscil), 0L, A_DEFLONG, 0);
    addfloat((method)oscil_float);
    addbang((method)oscil_bang);
	return(0);
}

void *oscil_new(long n)
{
	t_oscil *x = (t_oscil *)newobject(oscil_class);
	x->t_n = DEF_LOOP_SIZE;
	if (n != 0)
		x->t_n = n;
	x->t_ptr = 0;
	x->t_sr = sys_getsr();
	x->t_outlet = floatout(x);
	x->t_out = (t_float *)sysmem_newptr(sizeof(x->t_out) * (x->t_n + 1));
	oscil_filltable(x);

	return (x);
}

void oscil_float(t_oscil *x, t_float val)
{
	x->t_freq = val;
}

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


void oscil_bang(t_oscil *x)
{
	oscil_sin(x);
	oscil_trunc(x);
	oscil_interp(x);
}

void oscil_sin(t_oscil *x)
{
	double beg, end;
	int i, n;
	double p, incr = 2 * PI * x->t_freq / x->t_sr;
	
	clock_getftime(&beg);
	p = x->t_ptr; // starting phase
	
	for (i = 0; i < x->t_n; i++)
	{	
		float *out = x->t_out;
		n = x->t_n;
		
		while (n--) 
		{
			*out++ = sin(p);
			p += incr;
//			if (p > x->t_sr)
//				p -= x->t_sr;
		}
	}
	x->t_ptr = p; // ending phase
	clock_getftime(&end);

post("SIN: %5.3f msec per %d",(end - beg) / x->t_n, x->t_n);
}

void oscil_interp(t_oscil *x)
{
	double beg, end;
	int i, n, ip;
	float p, incr = TABSIZE * x->t_freq / x->t_sr;
	p = x->t_ptr; // starting phase
	
	clock_getftime(&beg);
	for (i = 0; i < x->t_n; i++)
	{	
		float *out = x->t_out;
		n = x->t_n;
		
	    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;
			if (p > TABSIZE)
				p -= TABSIZE;
		}
	}
	x->t_ptr = p; // ending phase
	clock_getftime(&end);

post("INTERP: %5.3f msec per %d", (end - beg) / x->t_n, x->t_n);
}

void oscil_trunc(t_oscil *x)
{
	double beg, end;
	int i, n;
	float p, incr = TABSIZE * x->t_freq / x->t_sr;
	p = x->t_ptr; // starting phase
	
	clock_getftime(&beg);
	for (i = 0; i < x->t_n; i++)
	{	
		float *out = x->t_out;
		n = x->t_n;
		
	    p = x->t_ptr;
		while (n--) 
		{
	
			*out++ = x->t_tab[(int)(p + 0.5)];
			p += incr;
			if (p > TABSIZE)
				p -= TABSIZE;
		}
	}
	x->t_ptr = p; // ending phase
	clock_getftime(&end);

post("ROUND: %5.3f msec per %d", (end - beg) / x->t_n, x->t_n);


}
/*

CodeWarrior data on PB 1.25GHz
SIN: 0.423814 msec per 1000
TRUNC: 0.088000 msec per 1000
INTERP: 0.189000 msec per 1000
SIN: 0.442289 msec per 1000
TRUNC: 0.077000 msec per 1000
INTERP: 0.255000 msec per 1000
SIN: 0.678037 msec per 1000
TRUNC: 0.109000 msec per 1000
INTERP: 0.25x->t_n msec per 1000
SIN: 0.330000 msec per 1000
TRUNC: 0.072000 msec per 1000
INTERP: 0.212000 msec per 1000
SIN: 0.323660 msec per 1000
TRUNC: 0.143000 msec per 1000
INTERP: 0.173000 msec per 1000

Xcode on PB 1.25GHz 2005/11/01
SIN: 0.432 msec per 5000
ROUND: 0.284 msec per 5000
INTERP: 0.484 msec per 5000

SIN: 0.872 msec per 10000
ROUND: 0.574 msec per 10000
INTERP: 0.967 msec per 10000
*/
