
XYscope []xy = new XYscope[49];


float           []channel = new float[49];
float           []channel_amp = new float[49];
int             []chan_index = new int [49];
int             []adsr_t = new int[49];
int             []t = new int[49];
float            attack;
float            decay;
float            sustain;
float            release;

int             activated_channels = 0;
int             timbre = 0;
int             filling = 0;

Star[]        stars = new Star[500];

// noise related
float[] yWave;
float[] xWave;

C c = new C();
C_sharp c_sharp = new C_sharp();
D d = new D();
D_sharp d_sharp = new D_sharp();
E e = new E();
F f = new F();
F_sharp f_sharp = new F_sharp();
G g = new G();
G_sharp g_sharp = new G_sharp();
A a = new A();
A_sharp a_sharp = new A_sharp();
B b = new B();

Draw_notes[] draw_notes_array = new Draw_notes[] {
    new Draw_notes() { public void draw_(XYscope xy, int i) { c.draw_C(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { c_sharp.draw_C_sharp(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { d.draw_D(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { d_sharp.draw_D_sharp(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { e.draw_E(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { f.draw_F(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { f_sharp.draw_F_sharp(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { g.draw_G(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { g_sharp.draw_G_sharp(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { a.draw_A(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { a_sharp.draw_A_sharp(xy, i); } },
    new Draw_notes() { public void draw_(XYscope xy, int i) { b.draw_B(xy, i); } },
};

int                n_point = 30;
float            x[] = new float[n_point];
float            y[] = new float[n_point];
float                rx[] = new float[n_point];
float                ry[] = new float[n_point];

void midi_polyphonic_synth(int status, int chan, int value)
{
      boolean swapped = false;
      
      if (status == -112)
      {
          for (int i = 0; i < 49; i++)
          {
              if (channel[i] < 10)
              {
                  channel[i] = pow(2, (chan - 69f) / 12) * 440;
                  channel_amp[i] = map(value, 0, 127, 0, 1);
                  channel_amp[i] = 1;
                  adsr_t[i] = 0;
                  t[i] = 0;
                  chan_index[i] = chan;
                  break;
                  
              }
          }
          activated_channels++;
      }
      if (status == -128)
      {

          for (int i = 0; i < 49; i++)
          {
              if(chan_index[i] == chan && swapped == false)
              {
                   channel[i] = 0;
                   channel_amp[i] = 0;
                   chan_index[i] = 0;
                   swapped = true;
              }
              if (swapped == true)
              {
                  channel[i] = channel[i + 1];
                  channel_amp[i] = channel_amp[i + 1];
                  chan_index[i] = chan_index[i+1];
                  adsr_t[i] = adsr_t[i + 1];
                  t[i] = t[i + 1];
                  if (channel[i] < 1)
                      break;
              }
          }          activated_channels--;
      }
      if (status == -80 && chan == 7)
      {
          timbre = round(map(value, 1, 128, 0, 4));
      }
      if (status == -79 && chan == 7)
      {
          filling = int(value * 1.5f);
      }
      if (status == -76 && chan == 7)
      {
          attack = value;
      }
      if (status == -75 && chan == 7)
      {
          decay = value;
      }
      if (status == -74 && chan == 7)
      {
          sustain = log(map(value, 0 , 127, 1, 2.718));
      }
      if (status == -73 && chan == 7)
      {
          release = value;
      }
}

void setup_polyphonic_synth() {
    for (int i = 0; i < 49; i++)
    {
        channel[i] = 0;
        channel_amp[i] = 0;
        chan_index[i] = 0;
        adsr_t[i] = 0;
        t[i] = 0;
    // initialize XYscope with default sound out
        xy[i] = new XYscope(this);
        xy[i].freq(0);
        xy[i].amp(0);
        xy[i].ellipseDetail(300);
        genNoiseWave(xy[i]);
    }
    for (int i = 0; i < n_point; i++)
    {
        rx[i] = random(-200, 100) / 100.0;
        ry[i] = random(-100, 100) / 100.0;
    }
    background(0);
    setup_star();
}

void draw_polyphonic_synth() {
    background(0);
    strokeWeight(2);
    for (int i = 0; i < activated_channels; i++)
    {
        
        xy[i].clearWaves();
        
        xy[i].freq(channel[i]);
        if(adsr_t[i] < attack)
        {
            xy[i].amp(adsr_t[i] / attack);
            adsr_t[i]++;
        }
        else
        {
            channel_amp[i] = 1f - min((float(adsr_t[i]) - attack) / decay, 1) * (1f - sustain);
            xy[i].amp(channel_amp[i]);
            if (min((float(adsr_t[i]) - attack) / decay, 1) < 1)
                adsr_t[i]++;
        }
        t[i]++;
        draw_notes_array[chan_index[i] % 12].draw_(xy[i], i);
        xy[i].buildWaves();
        xy[i].drawAll();
    }
    for (int i = activated_channels; i < 49; i++)
    {
        adsr_t[i] = 0;
        xy[i].clearWaves();
        xy[i].amp(0);
        xy[i].freq(0);
    }
    manage_star();
}
