/*
Based on the "stomp_shifter" from openmusiclabs;
this is a pitch shifter program.  it indexes through a sample
buffer at either a slower or faster rate than the incoming
samples.  the buffer boundaries are dealt with by having to samples
being played back simultaneously, each from opposite sides of the
buffer.  the volume of each is set by the distance to the boundary.
the rotary encoder sets the speed of playback.
*/

#include "mult16x16.h"

//defining harware resources.
#define LED 13
#define FOOTSWITCH 12
#define TOGGLE 2
#define PUSHBUTTON_1 A5
#define PUSHBUTTON_2 A4

//defining the output PWM parameters
#define PWM_FREQ 0x00FF // pwm frequency - 31.3KHz
#define PWM_MODE 0 // Fast (1) or Phase Correct (0)
#define PWM_QTY 2 // 2 PWMs in parallel
#define SIZE 1000 // make this smaller if it clicks

int buffer[SIZE]; // sample buffer
unsigned int location = 0; // current sample input position
unsigned int offset = 0; // playback sample offset from input
byte shift = 0x80;
unsigned int fractional = 0x80; // fractional sample rate
int data_buffer = 0x80;
int counter=0;
int toggle_position=0;

void setup() {
//setup IO
  pinMode(FOOTSWITCH, INPUT_PULLUP);
  pinMode(TOGGLE, INPUT_PULLUP);
  pinMode(PUSHBUTTON_1, INPUT_PULLUP);
  pinMode(PUSHBUTTON_2, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  
  // setup ADC
  ADMUX = 0x60; // left adjust, adc0, internal vcc
  ADCSRA = 0xe5; // turn on adc, ck/32, auto trigger
  ADCSRB = 0x07; // t1 capture for trigger
  DIDR0 = 0x01; // turn off digital inputs for adc0

  // setup PWM
  TCCR1A = (((PWM_QTY - 1) << 5) | 0x80 | (PWM_MODE << 1)); //
  TCCR1B = ((PWM_MODE << 3) | 0x11); // ck/1
  TIMSK1 = 0x20; // interrupt on capture interrupt
  ICR1H = (PWM_FREQ >> 8);
  ICR1L = (PWM_FREQ & 0xff);
  DDRB |= ((PWM_QTY << 1) | 0x02); // turn on outputs
  sei(); // turn on interrupts - not really necessary with arduino
}

void loop() 
{
  //Turn on the LED if the effect is ON.
  if (digitalRead(FOOTSWITCH)) digitalWrite(LED, HIGH); 
    else  digitalWrite(LED, LOW);
  
  //nothing else here, all happens in the Timer 1 interruption.
}

ISR(TIMER1_CAPT_vect) {
  
  // output data
  OCR1AL = ((data_buffer >> 8) + 0x80); 
  OCR1BL = data_buffer; // output the bottom byte
  
  // get ADC data
  byte temp1 = ADCL; // you need to fetch the low byte first
  byte temp2 = ADCH; // yes it needs to be done this way
  int input = ((temp2 << 8) | temp1) + 0x8000; // make a signed 16b value
  
 //BUTTONS
  counter++; //the pushbuttons are checked every 2500 times.
  if(counter==2500)
{ 

counter=0;
if (!digitalRead(PUSHBUTTON_2)) {
    shift++;
        if (shift >= 0xfe) shift = 0xfe;
      }
 if (!digitalRead(PUSHBUTTON_1)) {
    shift--;
        if (shift <= 1) shift = 1;
      }
    }

//Depending on the Toggle switch position, the effect is reseted)
  if(digitalRead(TOGGLE)!=toggle_position)
    {
    toggle_position = digitalRead(TOGGLE); //update the new value
    shift=0x80;
   }
  
  buffer[location] = input; // store incoming data
  location++; // increment storage location
  if (location >= SIZE) location = 0;  // boundary wrap
  unsigned int temp = location + offset; // find next sample
  if (temp >= SIZE) temp -= SIZE; // boundary wrap
  int output = buffer[temp];  // fetch sample
  temp += (SIZE >> 1);  // find sample on opposite side of buffer
  if (temp >= SIZE) temp -= SIZE; // boundary wrap
  int output2 = buffer[temp]; // fetch sample
  unsigned int distance; // find distance to buffer boundary
  if (offset > (SIZE >> 1)) distance = SIZE - offset;
  else distance = offset;
  int result; // average the 2 samples based on distance to boundary
  MultiSU16X16toH16(result, output, (distance << 7));
  MultiSU16X16toH16(output, output2, (((SIZE >> 1) - distance) << 7));
  output += result;
  fractional += shift; // increment offset
  if (fractional >= 0x0080) {
    offset += (fractional >> 7);
    fractional &= 0x007f;
  }
  if (offset >= SIZE) offset -= SIZE; // boundary wrap

  // save data
  data_buffer = output;
}