/*
 *   Pure Data Packet module. Standard sync rates.
 *   Copyright (c) by Tom Schouten <tom@zwizwa.be>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* pdp_metro
   this is the main audio/video sync module. use this if
   you need to produce synchronized raw output. this uses
   'industry standard' integer sampling ratios.

   it works like this. for example, the most awkward 30 * 999/1000 Hz rate.


*/

#include "pdp.h"

typedef struct _metro_struct
{
    t_object  x_obj;
    t_float   x_f;
    t_clock  *x_clock;
    t_outlet *x_outlet;

    t_int    x_count;      // current count for (upsampled) audio ticks
    t_int    x_den;        // video framerate denominator and nominator
    t_int    x_nom;

} t_metro;




static t_int *metro_perform(t_int *w);
#define DSP_ARG(type, name, source) type name = (type)source
static void metro_dsp(t_metro *x, t_signal **sp)
{
    dsp_add(metro_perform, 3, x, sp[0]->s_n, (int)sys_getsr());
}
static t_int *metro_perform(t_int *w){
    DSP_ARG(t_metro*, x,  w[1]);
    DSP_ARG(t_int,    n,  w[2]);
    DSP_ARG(t_int,    sr, w[3]);

    t_int period   = sr * x->x_nom;
    t_int upsample = x->x_den;

    t_int dobang = 0;
    
    //fprintf(stderr, "%d/%d\t%d\t%d\n", x->x_den, x->x_nom, x->x_count, period);
    x->x_count += n * upsample;
    while (x->x_count > period) {
	dobang = 1;
	x->x_count -= period;
    }
    if (dobang) clock_set(x->x_clock, 0);
    return w+4;
}
static void metro_tick(t_metro *x)
{
    outlet_bang(x->x_outlet);
}

static void metro_reset(t_metro *x){
    x->x_count = 0;
}
static void metro_fps(t_metro *x, t_float fden, t_float fnom)
{
    int den = (int)fden;
    int nom = (int)fnom;
    if (den < 1) den = 1;
    if (nom < 1) nom = 1;
    x->x_den = den;
    x->x_nom = nom;
}
static void metro_free(t_metro *x)
{
    clock_unset(x->x_clock);
}

static t_class *metro_class;



static void *metro_new(t_symbol __attribute__((unused)) *s,
                       int __attribute__((unused)) argc,
                       __attribute__((unused)) t_atom *argv)
{
    t_metro *x = (t_metro *)pd_new(metro_class);
    x->x_outlet = outlet_new(&x->x_obj, &s_bang);
    x->x_clock = clock_new(x, (t_method)metro_tick);
    x->x_count = 0;

    metro_fps(x, 1, 1);

    return (void *)x;
}


#ifdef __cplusplus
extern "C"
{
#endif


void pdp_metro_setup(void)
{


    metro_class = class_new(gensym("pdp_metro~"), (t_newmethod)metro_new,
			    (t_method)metro_free, sizeof(t_metro), 0, A_NULL);

    CLASS_MAINSIGNALIN(metro_class, t_metro, x_f);   
    class_addmethod(metro_class, (t_method)metro_tick,   gensym("tick"),  A_NULL);
    class_addmethod(metro_class, (t_method)metro_dsp,    gensym("dsp"),  A_NULL);
    class_addmethod(metro_class, (t_method)metro_fps,    gensym("fps"),  A_FLOAT, A_DEFFLOAT, A_NULL);


}

#ifdef __cplusplus
}
#endif
