#include <math.h>
#include <stdio.h>
#include <sys/soundcard.h>
#include "HarmOscBank.h"
#include "miditabl.h"
#include "MIDIInpt.h"
#include "RTWvOut.h"
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Value_Slider.H>
#include <FL/Fl_Slider.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Value_Output.H>

Fl_Value_Slider **Fa;	           // an array of amp sliders
#define DELTA 32

HarmOscBank *hosc;
int N=10;
double AMP = 0.0;  

Fl_Slider *F0;
Fl_Value_Output *V0=(Fl_Value_Output *)0;
Fl_Slider *Fl;
Fl_Value_Output *VFl=(Fl_Value_Output *)0;
Fl_Slider *Co;
Fl_Value_Output *VCo=(Fl_Value_Output *)0;
Fl_Slider *CF;
Fl_Value_Output *VCF=(Fl_Value_Output *)0;
Fl_Slider *IF;
Fl_Value_Output *VIF=(Fl_Value_Output *)0;
Fl_Value_Slider *Fvol=(Fl_Value_Slider *)0;

static void cb_Fvol(Fl_Value_Slider* o, void*) {
  AMP = o->value()/4;
}

static void cb_F0(Fl_Slider* o, void*) {
  double val = int(pow(900, o->value())*10);
  V0->value(val);
  V0->redraw();
  hosc->setFreq(val);
}

static void cb_Fl(Fl_Slider* o, void*) {
  double val = o->value();
  VFl->value(val);
  VFl->redraw();

  hosc->setTFlutter(val);

  CF->value(hosc->cflutter);
  VCF->value(hosc->cflutter);
  IF->value(hosc->iflutter);
  VIF->value(hosc->iflutter);
  CF->redraw(); IF->redraw();
  VCF->redraw(); VIF->redraw();
}

static void cb_Co(Fl_Slider* o, void*) {
  double val = o->value();
  VCo->value(val);
  VCo->redraw();

  hosc->setFlutterC(val);

  CF->value(hosc->cflutter);
  VCF->value(hosc->cflutter);
  IF->value(hosc->iflutter);
  VIF->value(hosc->iflutter);
  CF->redraw(); IF->redraw();
  VCF->redraw(); VIF->redraw();
}

static void cb_CF(Fl_Slider* o, void*) {
  double val = o->value();
  VCF->value(val);
  VCF->redraw();

  hosc->setCFlutter(val);

  Fl->value(hosc->tflutter);
  VFl->value(hosc->tflutter);
  Co->value(hosc->flutterc);
  VCo->value(hosc->flutterc);
  Fl->redraw(); Co->redraw();
  VFl->redraw(); VCo->redraw();
}

static void cb_IF(Fl_Slider* o, void*) {
  double val = o->value();
  VIF->value(val);
  VIF->redraw();

  hosc->setIFlutter(val);

  Fl->value(hosc->tflutter);
  VFl->value(hosc->tflutter);
  Co->value(hosc->flutterc);
  VCo->value(hosc->flutterc);
  Fl->redraw(); Co->redraw();
  VFl->redraw(); VCo->redraw();
}

Fl_Light_Button *Signal=(Fl_Light_Button *)0;

Fl_Value_Slider *Froll=(Fl_Value_Slider *)0;

static void cb_Froll(Fl_Value_Slider* o, long n) {
  int i;
  double val, A;
  val = o->value();
  hosc->setRolloff(val);
  for (i=0; i<n; i++)
  {
    A=exp(double(-val*i));
    hosc->setAmp(i, A);
    Fa[i]->value(A);
  }
}

static void cb_Amp(Fl_Value_Slider* o, long i) 
{
  double val = o->value();
  hosc->setAmp(i, val);
}

#define XDIST 40		// distance between adjacent sliders
#define XSIZE 40                // Slider dimensions
#define YSIZE 111

main(int argc, char** argv)
{
  int i, j;
  int xcur;
  char st[20];

  double freq=440, temp, temp2, out;

  //  printf("main 1\n");

  RTWvOut output;

  //  printf("main 2\n");

  if (argc>1) 
    N = atoi(argv[1]);
  if (N>MAXOSC) N = MAXOSC;

  //  printf("main 3\n");

  hosc = new HarmOscBank(N, freq);
  //  printf("main 3.01\n");
  hosc->setFreq(440.0);
  //  printf("main 3.02\n");
  hosc->setRolloff(1.0);
  hosc->setAmp(0.0);

  //  printf("main 3.1\n");

  //  printf("main 3.2\n");

  Fa = new (Fl_Value_Slider *)[N+1];

  xcur = 115;

  //  printf("main 4\n");

  Fl_Double_Window* wnd = new Fl_Double_Window(125+XDIST*N, 537, "Additive Synthesis");
  wnd->box(FL_UP_BOX);
  wnd->color(53);
  wnd->selection_color(34);
  wnd->labeltype(FL_NORMAL_LABEL);
  wnd->labelsize(12);
  wnd->labelcolor(1);

  char lblA[N+1][8];
  char lblF[N+1][8];

  //  printf("main 5\n");

  { Fl_Slider* o = F0 = new Fl_Slider(12, 45, XSIZE, 108, "F0");
    o->type(4);
    o->box(FL_FLAT_BOX);
    o->color(53);
    o->selection_color(0);
    o->labelsize(12);
    o->step(0.001);
    o->minimum(1.0);
    o->maximum(0.0);
    o->value(0.56);
    o->callback((Fl_Callback*)cb_F0);
  }

   { Fl_Value_Output* o = V0 = new Fl_Value_Output(12, 10, 28, XSIZE, "Hz");
     o->box(FL_FLAT_BOX);
     o->color(53);
     o->labeltype(FL_NO_LABEL);
     o->labelsize(12);
     o->step(0.001);
     o->minimum(9000);
     o->maximum(10);
     o->value(440);
     o->textsize(11);
     o->align(FL_ALIGN_RIGHT);
   }

  { Fl_Slider* o = Fl = new Fl_Slider(60, 45, XSIZE, 108, "Flutter");
    o->type(4);
    o->box(FL_FLAT_BOX);
    o->color(53);
    o->selection_color(0);
    o->labelsize(12);
    o->step(0.01);
    o->minimum(1000.0);
    o->maximum(0.0);
    o->value(0.0);
    o->callback((Fl_Callback*)cb_Fl);
  }

   { Fl_Value_Output* o = VFl = new Fl_Value_Output(60, 10, 28, XSIZE, " ");
     o->box(FL_FLAT_BOX);
     o->color(53);
     o->labeltype(FL_NO_LABEL);
     o->labelsize(12);
     o->step(0.01);
     o->minimum(1000.0);
     o->maximum(0.0);
     o->value(0.0);
     o->textsize(11);
     o->align(FL_ALIGN_RIGHT);
   }

  { Fl_Slider* o = Co = new Fl_Slider(108, 45, XSIZE, 108, "Corr");
    o->type(4);
    o->box(FL_FLAT_BOX);
    o->color(53);
    o->selection_color(0);
    o->labelsize(12);
    o->step(0.0001);
    o->minimum(1.0);
    o->maximum(0.0);
    o->value(1.0);
    o->callback((Fl_Callback*)cb_Co);
  }

   { Fl_Value_Output* o = VCo = new Fl_Value_Output(108, 10, 28, XSIZE, " ");
     o->box(FL_FLAT_BOX);
     o->color(53);
     o->labeltype(FL_NO_LABEL);
     o->labelsize(12);
     o->step(0.01);
     o->minimum(1.0);
     o->maximum(0.0);
     o->value(1.0);
     o->textsize(11);
     o->align(FL_ALIGN_RIGHT);
   }

  { Fl_Slider* o = CF = new Fl_Slider(156, 45, XSIZE, 108, "CFlutter");
    o->type(4);
    o->box(FL_FLAT_BOX);
    o->color(53);
    o->selection_color(0);
    o->labelsize(12);
    o->step(0.005);
    o->minimum(500.0);
    o->maximum(0.0);
    o->value(0.0);
    o->callback((Fl_Callback*)cb_CF);
  }

   { Fl_Value_Output* o = VCF = new Fl_Value_Output(156, 10, 28, XSIZE, " ");
     o->box(FL_FLAT_BOX);
     o->color(53);
     o->labeltype(FL_NO_LABEL);
     o->labelsize(12);
     o->step(0.005);
     o->minimum(500.0);
     o->maximum(0.0);
     o->value(0.0);
     o->textsize(11);
     o->align(FL_ALIGN_RIGHT);
   }

  { Fl_Slider* o = IF = new Fl_Slider(204, 45, XSIZE, 108, "IFlutter");
    o->type(4);
    o->box(FL_FLAT_BOX);
    o->color(53);
    o->selection_color(0);
    o->labelsize(12);
    o->step(0.005);
    o->minimum(500.0);
    o->maximum(0.0);
    o->value(0.0);
    o->callback((Fl_Callback*)cb_IF);
  }

   { Fl_Value_Output* o = VIF = new Fl_Value_Output(204, 10, 28, XSIZE, " ");
     o->box(FL_FLAT_BOX);
     o->color(53);
     o->labeltype(FL_NO_LABEL);
     o->labelsize(12);
     o->step(0.005);
     o->minimum(500.0);
     o->maximum(0.0);
     o->value(0.0);
     o->textsize(11);
     o->align(FL_ALIGN_RIGHT);
   }

  { Fl_Value_Slider* o = Fvol = new Fl_Value_Slider(12, 174, XSIZE, 133, "Volume");
    o->type(4);
    o->box(FL_FLAT_BOX);
    o->color(53);
    o->selection_color(0);
    o->labelsize(12);
    o->value(0.0);
    o->step(0.001);
    o->maximum(0);
    o->minimum(1);
    o->callback((Fl_Callback*)cb_Fvol);
  }

  { Fl_Value_Slider* o = Froll = new Fl_Value_Slider(60, 174, XSIZE, 133, "Rolloff");
    o->type(4);
    o->box(FL_FLAT_BOX);
    o->color(53);
    o->selection_color(0);
    o->labelsize(12);
    o->step(0.001);
    o->minimum(0);
    o->maximum(1);
    o->value(1.0);
    o->callback((Fl_Callback*)cb_Froll, (void *) N);
  }

  for(i=0; i<N; i++, xcur+=XDIST)
  {
    sprintf(lblA[i], "A%d", i);
    Fa[i]= new Fl_Value_Slider(xcur, 183, XSIZE, YSIZE, lblA[i]);
    Fa[i]->type(4);
    Fa[i]->box(FL_FLAT_BOX);
    Fa[i]->color(53);
    Fa[i]->selection_color(8);
    Fa[i]->labelsize(11);
    Fa[i]->minimum(1);
    Fa[i]->maximum(0);
    Fa[i]->step(0.001);
    Fa[i]->value(0.0);
    Fa[i]->callback((Fl_Callback*)cb_Amp, (void *) i);
    
  }

  //  printf("main 6\n");

  wnd->end();
  
  cb_Froll(Froll, N);
  wnd->show(0,(char**)0);

  { double nonoise, err, out;

  //  printf("main 7\n");

    while (Fl::check())
    {
      for (i=0;i<DELTA;i++) {

        out = AMP * hosc->tick();
        err = 0.0;

        output.tick(out);
      }
    }
  }
}
