#include <SPI.h>
#include <GD.h>
#include "debug.h"

// ----------------------------------------------------------------------
//     controller: buttons on Arduino pins 3,4,5,6 with 7 grounded
// ----------------------------------------------------------------------

static void controller_init()
{
  // Configure input pins with internal pullups
  byte i;
  for (i = 3; i < 7; i++) {
    pinMode(i, INPUT);
    digitalWrite(i, HIGH);
  }
  // drive pin 7 low
  pinMode(7, OUTPUT);
  digitalWrite(7, 0);
}

#define CONTROL_LEFT  1
#define CONTROL_RIGHT 2
#define CONTROL_UP    4
#define CONTROL_DOWN  8

static byte controller_sense()
{
  byte r = 0;
  if (!digitalRead(6))
    r |= CONTROL_LEFT;
  if (!digitalRead(5))
    r |= CONTROL_RIGHT;
  if (!digitalRead(4))
    r |= CONTROL_DOWN;
  if (!digitalRead(3))
    r |= CONTROL_UP;
  return r;
}

void setup()
{
  int i;

  controller_init();
  GD.begin();
  
  GD.ascii();
#if 0

  for (i = 0; i < 256; i++)
    GD.sprite(i, 400, 400, 0, 0, 0);
  GD.putstr(0, 0,"Audio demo");
#endif

  Serial.begin(1000000);
  return;

  for (i = 0; i < 16; i++) {
    GD.voice(i, 0, random(1000), 0, 0);
  }
  delay(1);
}

void voiceloop()
{
  byte i, j;
  for (i = 0; i < 17; i++) {
    Serial.print("Voice ");
    Serial.println(i, DEC);
    for (j = 0; j < 16; j++) {
      if (j == i)
        GD.voice(j, 0, 4 * 440, 255, 254);
      else
        GD.voice(j, 0, 0, 0, 0);
    }
    dump(VOICES, 4 * 32);
    delay(1000);
  }
}

void silence()
{
  int i;
  for (i = 0; i < 16; i++) {
    GD.voice(i, 0, 0, 0, 0);
  }
}

void announce(char *s)
{
  silence();
  GD.putstr(0, 10, "                                        ");
  delay(500);
  GD.putstr(0, 10, s);
  delay(500);
}

void percuss(int freq, byte decay)
{
  int t;
  for (t = 0; t < 1000; t++) {
    int amp = max((63 - (t >> decay)), 0);
    GD.voice(0, 1, 4 * freq - 2, amp, amp);
    GD.voice(1, 1, 4 * freq - 1, amp, amp);
    GD.voice(2, 1, 4 * freq + 1, amp, amp);
    GD.voice(3, 1, 4 * freq + 2, amp, amp);
    delay(1);
  }
}

void spread(byte ch, int freq)
{
  int i;
  ch = ch << 8;
  for (i = 0; i < 8; i++) {
    GD.voice(ch + i, 0, freq - 256 + (i << 6), 15, 16);
  }
}

void organ(int f0)
{
  byte choir[] = { 71, 32, 14, 75, 20, 40 };
  byte i;
  for (i = 0; i < 6; i++) {
    byte a = choir[i] >> 1;
    GD.voice(i, 0, f0 * (i + 1), a, a);
    GD.voice(8 + i, 0, f0 * (i + 1) - random(8), a, a);
  }
  delay(4000);
}

// JCB-SAWTOOTH-A
#define SINE 0
#define NOISE 1
void sawtooth_wave(int f0)
{
  GD.voice(0, SINE, f0,     100,    100);
  GD.voice(1, SINE, 2 * f0, 100/2,  100/2);
  GD.voice(2, SINE, 3 * f0, 100/3,  100/3);
  GD.voice(3, SINE, 4 * f0, 100/4,  100/4);
}
// JCB-SAWTOOTH-B

// JCB-SQUARE-A
void squarewave(int f0)
{
  GD.voice(0, SINE, f0,     100,    100);
  GD.voice(1, SINE, 3 * f0, 100/3,  100/3);
  GD.voice(2, SINE, 5 * f0, 100/5,  100/5);
  GD.voice(3, SINE, 7 * f0, 100/7,  100/7);
}
// JCB-SQUARE-B

#include "sax.h"
#include "cowbell.h"
#include "piano.h"

#include "soundbuffer.h"

static void loopsample()
{
  cli();
  for (;;) {
    // JCB-SIMPLESAMPLE-A
    PROGMEM prog_uchar *p;
    for (p = cowbell; p < cowbell + sizeof(cowbell); p++) {
      byte v = pgm_read_byte_near(p);
      GD.wr(SAMPLE_L + 1, v);
      GD.wr(SAMPLE_R + 1, v);
      delayMicroseconds(125);
    }
    // JCB-SIMPLESAMPLE-B
  }
}

#define SOUNDBUFFER 0x3f00
int notes[12] = {
// >>> [int(1000*pow(2, i/12.)) for i in range(12)]
1000, 1059, 1122, 1189, 1259, 1334, 1414, 1498, 1587, 1681, 1781, 1887
};

void loop()
{
#if 0
  // Attempt to get clicks 
  GD.voice(0, SINE, 0x800, 255, 255);
  float dummy = 0;
  for (;;) {
    GD.wr(VOICES, 0xff);
    GD.wr(VOICES, 0x00);
#if 0
    int n = random(100);
    while (n--) {
      ++dummy;
    }
    Serial.println(dummy, DEC);
#else
    delay(100 + random(10));
#endif
  }
  sawtooth_wave(4 * 440);
  // squarewave(4 * 440);
  for (;;)
    ;
#endif

#if 0
  int i;
  for (i = 0; i < 256; i++) {
    float th = 10 * i * 3.1415 * 2 / 256;
    GD.wr(SOUNDBUFFER + i, (char)(127 * sin(th)));
  }
  dump(SOUNDBUFFER, 256);
  for (;;) ;
#endif

  // JCB-BUFFERSAMPLE-A
  GD.microcode(soundbuffer_code, sizeof(soundbuffer_code));

  byte writepointer = 0;
  PROGMEM prog_uchar *p = cowbell;
  for (;;) {
    byte readpointer = GD.rd(COMM+0);
    byte fullness = writepointer - readpointer;
    while (fullness < 254) {
      char v = pgm_read_byte_near(p);
      GD.wr(SOUNDBUFFER + writepointer++, v);
      if (++p == (cowbell + sizeof(cowbell))) {
        p = cowbell;
      }
      fullness++;
    }
  }
  // JCB-BUFFERSAMPLE-B

#if 0
  byte writepointer = 0;
  byte readpointer, fullness;
  PROGMEM prog_uchar *p0 = piano;
  PROGMEM prog_uchar *p1 = cowbell;
  int scaling = 0;
  int rate = 1000;
  for (;;) {
    readpointer = GD.rd(COMM+0);
    fullness = writepointer - readpointer;
    while (fullness < 200) {
      char v0, v1;
      v0 = pgm_read_byte_near(p0);
      v1 = pgm_read_byte_near(p1);
      GD.wr(SOUNDBUFFER + writepointer++, (v0 + v1) >> 1);
      scaling += rate;
      while (scaling > 1000) {
        scaling -= 1000;
        if (++p0 == (piano + sizeof(piano))) {
          p0 = piano;
          rate = notes[random(12)];
        }
      }
      if (++p1 == (cowbell + sizeof(cowbell))) {
        p1 = cowbell;
      }
      fullness++;
    }
  }
#endif

#if 0
  announce("Explosion");
  percuss(5, 3);

  announce("organ");
  organ(4 * 220);

  announce("Cymbal");
#endif

  static byte drum;
  static int f;
  static byte ctl;

  byte co = controller_sense();
  if (co && (drum == 0)) {
    if (co & CONTROL_LEFT)
      ctl = 0;
    else if (co & CONTROL_RIGHT)
      ctl = 1;
    else if (co & CONTROL_DOWN)
      ctl = 2;
    else if (co & CONTROL_UP)
      ctl = 3;
    drum = 1;
    f = ctl * 110;

#if 0
    byte t;
    for (t = 0; t < 10; t++) {
      float ft = t / 100.0;
      float amp = max(0, 1 - 24 * ft);
      GD.voice(0, 1, f, 63 * amp, 63 * amp);
      GD.voice(1, 0, f, 127 * amp, 127 * amp);
    }
#endif
  }
#define SOUNDCYCLE(state) ((state) = v ? ((state) + 1) : 0)

#if 1
  if (drum) {
    byte v = max(0, 127 - (drum - 1) * 40);
    f = 220 + 220 * ctl;
    GD.voice(0, 1, 2 * f, v / 8, v / 8);
    GD.voice(1, 0, f, v, v);
    SOUNDCYCLE(drum);
  }
#endif
#if 0
  if (drum) {
    byte v = max(0, 48 - (drum - 1) * 3);
    GD.voice(2, 0, f, v, v);
    GD.voice(3, 1, f/8, v/2, v/2);
    SOUNDCYCLE(drum);
  }
#endif
#if 0
  if (drum) {
    Serial.println((50 + 50 * ctl), DEC);
    byte v = max(0, 64 - (drum - 1) * 8);
    GD.voice(2, 0, 4 * 4000 - 700 * drum, v/2, v/2);
    GD.voice(3, 1, 1000 - 100 * drum, v, v);
    SOUNDCYCLE(drum);
  }
#endif
#if 0
  if (drum) {
    byte v = drum < 9 ? 63 : 0;
    float notes[] = {49, 51.91, 55.0, 58.27};
    // float notes[] = {16.35, 17.32,18.35,19.45};
    // float notes[] = {32.7, 34.65, 36.71, 41.20};
    int f0 = int(4 * notes[ctl]);
    // int f0 = ctl ? 163 : 130;
    byte partials[] = { 71, 32, 14, 75, 20, 40 };
    byte i;
    for (i = 0; i < 6; i++) {
      byte a = (v * partials[i]) >> 7;
      GD.voice(i, 0, f0 * (i + 1), a, a);
    }
    SOUNDCYCLE(drum);
  }
#endif

  GD.waitvblank();
  GD.waitvblank();
}
