// Euclidean rhythms (http://cgm.cs.mcgill.ca/~godfried/publications/banff.pdf)
// Author: Peter Sovietov

desc:Euclidean rhythms
slider1:5<1,100,1>pulses
slider2:13<1,100,1>length
slider3:0<0,100,1>rotate
slider4:0.5<0.1,4>pulse duration (beats)
slider5:48<0,127,1{0: C-1,1: C#/Db-1,2: D-1,3: D#/Eb-1,4: E-1,5: F-1,6: F#/Gb-1,7: G-1,8: G#/Ab-1,9: A-1,10: A#/Bb-1,11: B-1,12: C0,13: C#/Db0,14: D0,15: D#/Eb0,16: E0,17: F0,18: F#/Gb0,19: G0,20: G#/Ab0,21: A0,22: A#/Bb0,23: B0,24: C1,25: C#/Db1,26: D1,27: D#/Eb1,28: E1,29: F1,30: F#/Gb1,31: G1,32: G#/Ab1,33: A1,34: A#/Bb1,35: B1,36: C2,37: C#/Db2,38: D2,39: D#/Eb2,40: E2,41: F2,42: F#/Gb2,43: G2,44: G#/Ab2,45: A2,46: A#/Bb2,47: B2,48: C3,49: C#/Db3,50: D3,51: D#/Eb3,52: E3,53: F3,54: F#/Gb3,55: G3,56: G#/Ab3,57: A3,58: A#/Bb3,59: B3,60: C4,61: C#/Db4,62: D4,63: D#/Eb4,64: E4,65: F4,66: F#/Gb4,67: G4,68: G#/Ab4,69: A4,70: A#/Bb4,71: B4,72: C5,73: C#/Db5,74: D5,75: D#/Eb5,76: E5,77: F5,78: F#/Gb5,79: G5,80: G#/Ab5,81: A5,82: A#/Bb5,83: B5,84: C6,85: C#/Db6,86: D6,87: D#/Eb6,88: E6,89: F6,90: F#/Gb6,91: G6,92: G#/Ab6,93: A6,94: A#/Bb6,95: B6,96: C7,97: C#/Db7,98: D7,99: D#/Eb7,100: E7,101: F7,102: F#/Gb7,103: G7,104: G#/Ab7,105: A7,106: A#/Bb7,107: B7,108: C8,109: C#/Db8,110: D8,111: D#/Eb8,112: E8,113: F8,114: F#/Gb8,115: G8,116: G#/Ab8,117: A8,118: A#/Bb8,119: B8,120: C9,121: C#/Db9,122: D9,123: D#/Eb9,124: E9,125: F9,126: F#/Gb9,127: G9}>note
slider6:64<1,127,1>velocity
slider7:1<1,16,1>channel
slider8:0<0,1,1{off,on}>trigger mode
in_pin:none
out_pin:none
@init
time_counter = 0;
note_on = 0;
old_mode = -1;
@slider
pulses_slider = slider1;
length_slider = slider2;
(
  left_len = 1;
  right_len = 1;
  left_rep = max(pulses_slider, 1);
  right_rep = max(length_slider, left_rep + 1) - left_rep;
  left = 0;
  right = left_rep + right_rep;
  left[0] = 1;
  right[0] = 0;
  while(
    memcpy(left + left_len, right, right_len);
    (left_rep > right_rep) ? (
      memcpy(right, left, left_len);
      left_len += right_len;
      right_len = left_len - right_len;
      right_rep = left_rep - right_rep;
      left_rep -= right_rep;
    ) : (
      left_len += right_len;
      right_rep -= left_rep;
    );
    right_rep > 1;
  );
  i = 1;
  (i < left_rep) ? (
    while(
      memcpy(left + (left_len * i), left, left_len);
      i += 1;
      i < left_rep;
    );
  );
  i = 1;
  (i < right_rep) ? (
    while(
      memcpy(right + (right_len * i), right, right_len);
      i += 1;
      i < right_rep;
    );
  );
  left_len *= left_rep;
  right_len *= right_rep;
  memcpy(left + left_len, right, right_len);
  left_len += right_len;
);
rotate_slider = slider3 % left_len;
duration_slider = slider4;
note_slider = slider5 & 127;
velocity_slider = slider6 & 127;
channel_slider = (slider7 - 1) & 15;
mode_slider = slider8;
note_msg23 = note_slider | (velocity_slider << 8);
(mode_slider != old_mode) ? (
  old_mode = mode_slider;
  (mode_slider) ? (
    play = 0;
  ) : (
    left_pointer = rotate_slider;
    play = 1;
  );
);
@block
(midirecv(offs, msg1, msg23)) ? (
  while(
    (mode_slider) ? (
      ((
        ((msg1 & 240) == 144) && ((msg23 & 32512) != 0);
      ) && ((msg23 & 127) == note_slider)) ? (
        left_pointer = rotate_slider;
        note_msg23 = msg23;
        play = 1;
      ) : (
        ((
          ((msg1 & 240) == 128) || (((msg1 & 240) == 144) && ((msg23 & 32512) == 0));
        ) && ((msg23 & 127) == note_slider)) ? (
          play = 0;
        ) : (
          midisend(offs, msg1, msg23);
        );
      );
    ) : (
      midisend(offs, msg1, msg23);
    );
    midirecv(offs, msg1, msg23);
  );
);
time_counter -= samplesblock;
(time_counter < 0) ? (
  (note_on) ? (
    midisend(time_counter + samplesblock, 144 | channel_slider, note_slider);
    note_on = 0;
  );
  (play) ? (
    (left[left_pointer]) ? (
      midisend(time_counter + samplesblock, 144 | channel_slider, note_msg23);
      note_on = 1;
    );
    left_pointer = (left_pointer + 1) % left_len;
  );
  time_counter += (
    max((1 / (tempo / 60)) * duration_slider * srate, samplesblock);
  );
);
@gfx
gfx_clear = 16777215;
(
  gfx_x = (gfx_w / 2);
  gfx_y = (gfx_h / 2);
);
(
  (
    gfx_r = 0;
    gfx_g = 0;
    gfx_b = 0;
    gfx_a = 1;
  );
  x0 = gfx_x;
  y0 = gfx_y;
  (
    gfx_x = x0;
    gfx_y = (y0 - (min(gfx_w, gfx_h) / 2.5));
  );
  i = 1;
  (i < left_len) ? (
    while(
      angle = (2 * 3.141592653589793 * i) / left_len;
      x = x0 + ((min(gfx_w, gfx_h) / 2.5) * sin(angle));
      y = y0 - ((min(gfx_w, gfx_h) / 2.5) * cos(angle));
      gfx_lineto(x, y, 1);
      i += 1;
      i < left_len;
    );
  );
  (left_len > 2) ? (
    gfx_lineto(x0, y0 - (min(gfx_w, gfx_h) / 2.5), 1);
  );
);
(
  gfx_x = (gfx_w / 2);
  gfx_y = (gfx_h / 2);
);
(
  x0 = gfx_x;
  y0 = gfx_y;
  i = 0;
  (i < left_len) ? (
    while(
      angle = (2 * 3.141592653589793 * i) / left_len;
      x = x0 + ((min(gfx_w, gfx_h) / 2.5) * sin(angle));
      y = y0 - ((min(gfx_w, gfx_h) / 2.5) * cos(angle));
      p = (rotate_slider + i) % left_len;
      (p == left_pointer) ? (
        (
          gfx_r = 1;
          gfx_g = 0;
          gfx_b = 0;
          gfx_a = 1;
        );
      ) : (
        (
          gfx_r = 0;
          gfx_g = 0;
          gfx_b = 0;
          gfx_a = 1;
        );
      );
      (left[p]) ? (
        (
          (
            gfx_x = (x - 5);
            gfx_y = (y - 5);
          );
          gfx_rectto(x + 9, y + 9);
        );
      ) : (
        (
          (
            gfx_x = (x - 5);
            gfx_y = (y - 5);
          );
          gfx_rectto(x + 10, y + 10);
          (
            gfx_r = 1;
            gfx_g = 1;
            gfx_b = 1;
            gfx_a = 1;
          );
          (
            gfx_x = (x - 4);
            gfx_y = (y - 4);
          );
          gfx_rectto(x + 9, y + 9);
        );
      );
      i += 1;
      i < left_len;
    );
  );
);
