/*
Copyright (C) 2004 Antoine Rousseau
all material Copyright (c) 1997-1999 Miller Puckette.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

/* a "sweet" saw generator, as described in pddoc/3.audio/K05.bandlimited .*/

#include "m_pd.h"
#include "math.h"
#include <stdlib.h>

#define UNITBIT32 1572864.  /* 3*2^19; bit 32 has place value 1 */

#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) \
    || defined(__OpenBSD__)
#include <machine/endian.h>
#endif

#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) || \
    defined(ANDROID)
#include <endian.h>
#endif

#ifdef __MINGW32__
#include <sys/param.h>
#endif

#ifdef _MSC_VER
/* _MSVC lacks BYTE_ORDER and LITTLE_ENDIAN */
#define LITTLE_ENDIAN 0x0001
#define BYTE_ORDER LITTLE_ENDIAN
#endif

#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN)
#error No byte order defined
#endif

#if BYTE_ORDER == LITTLE_ENDIAN
# define HIOFFSET 1
# define LOWOFFSET 0
#else
# define HIOFFSET 0    /* word offset to find MSB */
# define LOWOFFSET 1    /* word offset to find LSB */
#endif

union tabfudge
{
    double tf_d;
    int32_t tf_i[2];
};


/* -------------------------- ssaw~ ------------------------------ */
static t_class *ssaw_class, *scalarssaw_class;
static float ssaw_array[1002];
#define SAW_ARRAY_LEN 1002

typedef struct _ssaw
{
    t_object x_obj;
    //from phasor~:
    double x_phase;
    float x_conv;
    float x_f;	    /* scalar frequency */
    float x_band;	/* band limit (Hertz)*/
} t_ssaw;

static void *ssaw_new(t_floatarg f)
{
    t_ssaw *x = (t_ssaw *)pd_new(ssaw_class);
    x->x_f = f;
    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1"));
    x->x_phase = 0;
    x->x_conv = 0;
    x->x_band = 22000;
    outlet_new(&x->x_obj, gensym("signal"));
    return (x);
}

static t_int *ssaw_perform(t_int *w)
{
    t_ssaw *x = (t_ssaw *)(w[1]);
    t_float *in = (t_float *)(w[2]);
    t_float *out = (t_float *)(w[3]);
    int i,n = (int)(w[4]);
    double dphase = x->x_phase + UNITBIT32;
    union tabfudge tf;
    int normhipart;
    float conv = x->x_conv;
    float band=x->x_band*.33;
    float *buf = ssaw_array;

    tf.tf_d = UNITBIT32;
    normhipart = tf.tf_i[HIOFFSET];
    tf.tf_d = dphase;

    for (i = 0; i < n; i++)
        //while (n--)
    {
        float phase,band2,findex /*= *in++*/;
        int index /*= findex*/;
        float frac,  a,  b,  c,  d, cminusb, *fp;

        tf.tf_i[HIOFFSET] = normhipart;
        band2=abs(*in);
        if(band2>999999) band2=999999;
        else if(band2<1) band2=1;
        band2=band/band2;
        dphase += *in++ * conv;
        /**out++*/
        phase = (tf.tf_d - UNITBIT32)-0.5;
        tf.tf_d = dphase;

        findex=phase*band2;
        if(findex>0.5) findex=0.5;
        else if(findex<-0.5) findex=-0.5;

        /*findex=findex*1000+501;
        index=findex;*/
        /*if (index < 1)
            index = 1, frac = 0;
        else if (index > maxindex)
            index = maxindex, frac = 1;
        else*/
        frac = findex - index;
        /*fp = buf + index;
        a = fp[-1];
        b = fp[0];
        c = fp[1];
        d = fp[2];
        cminusb = c-b;
        *out++ = 0.5+ phase - (
        		 b + frac * ( cminusb - 0.1666667f * (1.-frac) * (
        		(d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)))
        		);*/
        *out++ = 0.5+ phase - buf[(int)(findex*1000+501)];
    }

    tf.tf_i[HIOFFSET] = normhipart;
    x->x_phase = tf.tf_d - UNITBIT32;
    return (w+5);
}

static void ssaw_dsp(t_ssaw *x, t_signal **sp)
{
    x->x_conv = 1./sp[0]->s_sr;
    dsp_add(ssaw_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
}

static void ssaw_ft1(t_ssaw *x, t_float f)
{
    x->x_phase = f;
}

static void ssaw_initarray(void)
{
    int i;
    float j;

    for(i=0; i<1002; i++)
    {
        j=(i-1)*M_PI/1000.0; //period 2000 sample, 1 sample back phase
        ssaw_array[i]= 0.57692*
                       (-1*cos(j) + 0.333333*cos(j*3.0) -0.2* cos(j*5.0));
    }
}

void ssaw_tilde_setup(void)
{
    ssaw_class = class_new(gensym("ssaw~"), (t_newmethod)ssaw_new, 0,
                           sizeof(t_ssaw), 0, A_DEFFLOAT, 0);
    CLASS_MAINSIGNALIN(ssaw_class, t_ssaw, x_f);
    class_addmethod(ssaw_class, (t_method)ssaw_dsp, gensym("dsp"), 0);
    class_addmethod(ssaw_class, (t_method)ssaw_ft1,
                    gensym("ft1"), A_FLOAT, 0);
    ssaw_initarray();

}
