/**
 * soundShell.c
 *
 * Written by Evan Raskob 2002-2003
 * :: soundshell [at] lowfrequency [dot] org ::
 * For use with the SoundShell instrument.
 * http://lowfrequency.org/itp/NIME03/NIME03.html
 *
 * Platform: PIC16F876
 * Uses 2 Sharp GP212 IR Rangers (on pins A0-A1)
 * and 4 momentary magnetic switches (on pins B1-B4)
 *
 * Not for use without permission of the author.
 */


/* Standard Include for 16F876 Chip */
#include <16F876.h>

/* library of midi functions (expanded in midi.c) */
#include "midi.h"

#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT

/* Delay for 20 mhz crystal */
#use delay (clock=20000000)

/* Setup buit-in RS232 serial ports to work work at MIDI speeds */
#use rs232(baud=MIDI_BAUD_RATE, xmit=PIN_C6,rcv=PIN_C7)

int sendByte (char byteToSend) {
        printf("%c", byteToSend);
        return 0;
}

/** DEBUG **/
//Setup buit-in RS232 serial ports to work work at serial port speeds
#use rs232(baud=9600, xmit=PIN_C4,rcv=PIN_C5)
/** END DEBUG **/

#use standard_io(B)

// send a midi NOTE ON message from the uart of the form [0x9n, note, vel]
// where n is the midi channel from 0-F, note and vel are 7-bit numbers
int midiNoteOn(char channel, char note, char vel) {
        sendByte(MIDI_NOTE_ON | (channel & MIDI_CHANNEL_MASK));
        sendByte(MIDI_DATA_MASK & note);
        sendByte(MIDI_DATA_MASK & vel);
        return 0;
}

// send a midi NOTE ON message from the uart of the form [0x9n, note, velocity]
// where n is the midi channel from 0-F, note is a 7-bit number and velocity is 0
int midiNoteOFF(char channel, char note) {
        sendByte(MIDI_NOTE_ON | (channel & MIDI_CHANNEL_MASK));
        sendByte(MIDI_DATA_MASK & note);
        sendByte(MIDI_DATA_MASK & 0);
        return 0;
}

// send a midi POLYPHONIC AFTERTOUCH message from the uart of the form [0xCn, controller, value]
// where n is the midi channel from 0-F, note and pressure are 7-bit numbers
int midiControlChange(char channel, char controller, char value) {
        sendByte(MIDI_CONTROL_CHANGE | (channel & MIDI_CHANNEL_MASK));
        sendByte(MIDI_DATA_MASK & controller);
        sendByte(MIDI_DATA_MASK & value);
        return 0;
}

int midiPolyTouch(char channel, char note, char pressure) {
        sendByte(MIDI_POLY_TOUCH | (channel & MIDI_CHANNEL_MASK));
        sendByte(MIDI_DATA_MASK & note);
        sendByte(MIDI_DATA_MASK & pressure);
        return 0;
}

// send a midi CHANNEL AFTERTOUCH message from the uart of the form [0xDn, pressure]
// where n is the midi channel from 0-F, and pressure is a 7-bit number
int midiChannelTouch(char channel, char pressure) {
        sendByte(MIDI_CHANNEL_TOUCH | (channel & MIDI_CHANNEL_MASK));
        sendByte(MIDI_DATA_MASK & pressure);
        return 0;
}


// send a midi PITCH BEND message from the uart of the form [0xEn, bendLSB, bendMSB ]
// where n is the midi channel from 0-F, and bendLSB and bendMSB are 7-bit numbers
// note that MIDI devices normally pack together bendLSB and bendMSB to make a 14-bit number
int midiPitchBend(char channel, char bendLSB, char bendMSB) {
        sendByte(MIDI_PITCH_BEND | (channel & MIDI_CHANNEL_MASK));
        sendByte(MIDI_DATA_MASK & bendLSB);
        sendByte(MIDI_DATA_MASK & bendMSB);
        return 0;
}


int fixAdcVal(adcval) {
   if (adcval < 0) adcval = -adcval;
   if (adcval > 127) adcval = 127;

   return adcval;
}


main()
{

   int adcval = 0;
   // beat per minute
   const int bpm = 120;
   // (calculated) fraction of milliseconds per beat based on beats per minute
   const int mspb =  150;  //(int) ((1000/16)*1/(120*60));

   // 4 magnetic momentary switches
   // hooked up to pins B1..B4
   int pin1val = 0;
   int pin2val = 0;
   int pin3val = 0;
   int pin4val = 0;

   int pintmp  = 0;

   //DEBUG:
   //output_high(PIN_B0)

   delay_ms(1000);  //  delay to start


   /**
    * This next block sets up RA3 and RA2 as reference pins
    * (one must be shorted to ground, other to power)
    * and A0 and A1 as the two ADC input pins
    */
   setup_adc(ADC_CLOCK_DIV_32);
   setup_adc_ports(RA0_RA1_ANALOG_RA3_RA2_REF);
   set_adc_channel(0);  // set to read from PIN_A0


   // DEBUG: print to console
   printf("Started.");

   while (TRUE)
   {

	/** DEBUG ******
      //channel, note, velocity
      midiNoteOn(0x9A, 0x33, 0x48);
      delay_ms(500);
      //channel, note
      midiNoteOff(0x9A, 0x33);
     ** END DEBUG **/

      delay_us(100);
      //read infrared ranger (GP212) on PIN_A0
      set_adc_channel(0);
      //need to delay after reading
      delay_ms(mspb);

      adcval = fixAdcVal(read_adc());


      /** DEBUG ******
      printf("ADC0 reading: %d\n", adcval);
       ** END DEBUG **/

	  //channel, controller#, value
      midiControlChange(10, 81, adcval);

      set_adc_channel(1); // read from PIN_A1
      //delay a fraction
      delay_ms(mspb);

     adcval = fixAdcVal(read_adc());

      //channel 10 is really channel 11 because channel#'s start at 0
      midiControlChange(10, 80, adcval);

      //printf("ADC1 reading: %d\n", adcval);

      //magnetic sensors
      pintmp = input(PIN_B1);
      if (pintmp != pin1val) {
        //switch state has changed
        pin1val = pintmp;
        if (pin1val) midiControlChange(10, 1, 1); //printf("switch1");
      }

     pintmp = input(PIN_B2);
      if (pintmp != pin2val) {
        //switch state has changed
        pin2val = pintmp;
        if (pin2val) midiControlChange(10, 1, 2); //printf("switch2");
      }

     pintmp = input(PIN_B3);
      if (pintmp != pin3val) {
        //switch state has changed
        pin3val = pintmp;
        if (pin3val) midiControlChange(10, 1, 3); //printf("switch3");
      }

     pintmp = input(PIN_B4);
      if (pintmp != pin4val) {
        //switch state has changed
        pin4val = pintmp;
        if (pin4val) midiControlChange(10, 1, 4); //printf("switch4");
      }

      }

 }

