15 Nisan 2019 Pazartesi

ARDUINO PİYANO





#define TEST_PATTERN_INTRO

//#define FILTER_LPF_NONE
#define FILTER_LPF_HACK


// table of 256 sine values / one sine period / stored in flash memory
char sine256[256]  __attribute__ ((aligned(256))) = {
0 , 3 , 6 , 9 , 12 , 15 , 18 , 21 , 24 , 27 , 30 , 33 , 36 , 39 , 42 , 45 ,
48 , 51 , 54 , 57 , 59 , 62 , 65 , 67 , 70 , 73 , 75 , 78 , 80 , 82 , 85 , 87 ,
89 , 91 , 94 , 96 , 98 , 100 , 102 , 103 , 105 , 107 , 108 , 110 , 112 , 113 , 114 , 116 ,
117 , 118 , 119 , 120 , 121 , 122 , 123 , 123 , 124 , 125 , 125 , 126 , 126 , 126 , 126 , 126 ,
127 , 126 , 126 , 126 , 126 , 126 , 125 , 125 , 124 , 123 , 123 , 122 , 121 , 120 , 119 , 118 ,
117 , 116 , 114 , 113 , 112 , 110 , 108 , 107 , 105 , 103 , 102 , 100 , 98 , 96 , 94 , 91 ,
89 , 87 , 85 , 82 , 80 , 78 , 75 , 73 , 70 , 67 , 65 , 62 , 59 , 57 , 54 , 51 ,
48 , 45 , 42 , 39 , 36 , 33 , 30 , 27 , 24 , 21 , 18 , 15 , 12 , 9 , 6 , 3 ,
0 , -3 , -6 , -9 , -12 , -15 , -18 , -21 , -24 , -27 , -30 , -33 , -36 , -39 , -42 , -45 ,
-48 , -51 , -54 , -57 , -59 , -62 , -65 , -67 , -70 , -73 , -75 , -78 , -80 , -82 , -85 , -87 ,
-89 , -91 , -94 , -96 , -98 , -100 , -102 , -103 , -105 , -107 , -108 , -110 , -112 , -113 , -114 , -116 ,
-117 , -118 , -119 , -120 , -121 , -122 , -123 , -123 , -124 , -125 , -125 , -126 , -126 , -126 , -126 , -126 ,
-127 , -126 , -126 , -126 , -126 , -126 , -125 , -125 , -124 , -123 , -123 , -122 , -121 , -120 , -119 , -118 ,
-117 , -116 , -114 , -113 , -112 , -110 , -108 , -107 , -105 , -103 , -102 , -100 , -98 , -96 , -94 , -91 ,
-89 , -87 , -85 , -82 , -80 , -78 , -75 , -73 , -70 , -67 , -65 , -62 , -59 , -57 , -54 , -51 ,
-48 , -45 , -42 , -39 , -36 , -33 , -30 , -27 , -24 , -21 , -18 , -15 , -12 , -9 , -6 , -3
};

char square256[256]   __attribute__ ((aligned(256)))  = {
127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 ,
127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 ,
127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 ,
127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 ,
127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 ,
127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 ,
127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 ,
127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 ,
-127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 ,
-127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 ,
-127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 ,
-127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 ,
-127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 ,
-127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 ,
-127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 ,
-127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127 , -127
};
char triangle256[256]   __attribute__ ((aligned(256)))  = {
-127 , -125 , -123 , -121 , -119 , -117 , -115 , -113 , -111 , -109 , -107 , -105 , -103 , -101 , -99 , -97 ,
-95 , -93 , -91 , -89 , -87 , -85 , -83 , -81 , -79 , -77 , -75 , -73 , -71 , -69 , -67 , -65 ,
-63 , -61 , -59 , -57 , -55 , -53 , -51 , -49 , -47 , -45 , -43 , -41 , -39 , -37 , -35 , -33 ,
-31 , -29 , -27 , -25 , -23 , -21 , -19 , -17 , -15 , -13 , -11 , -9 , -7 , -5 , -3 , -1 ,
1 , 3 , 5 , 7 , 9 , 11 , 13 , 15 , 17 , 19 , 21 , 23 , 25 , 27 , 29 , 31 ,
33 , 35 , 37 , 39 , 41 , 43 , 45 , 47 , 49 , 51 , 53 , 55 , 57 , 59 , 61 , 63 ,
65 , 67 , 69 , 71 , 73 , 75 , 77 , 79 , 81 , 83 , 85 , 87 , 89 , 91 , 93 , 95 ,
97 , 99 , 101 , 103 , 105 , 107 , 109 , 111 , 113 , 115 , 117 , 119 , 121 , 123 , 125 , 127 ,
129 , 127 , 125 , 123 , 121 , 119 , 117 , 115 , 113 , 111 , 109 , 107 , 105 , 103 , 101 , 99 ,
97 , 95 , 93 , 91 , 89 , 87 , 85 , 83 , 81 , 79 , 77 , 75 , 73 , 71 , 69 , 67 ,
65 , 63 , 61 , 59 , 57 , 55 , 53 , 51 , 49 , 47 , 45 , 43 , 41 , 39 , 37 , 35 ,
33 , 31 , 29 , 27 , 25 , 23 , 21 , 19 , 17 , 15 , 13 , 11 , 9 , 7 , 5 , 3 ,
1 , -1 , -3 , -5 , -7 , -9 , -11 , -13 , -15 , -17 , -19 , -21 , -23 , -25 , -27 , -29 ,
-31 , -33 , -35 , -37 , -39 , -41 , -43 , -45 , -47 , -49 , -51 , -53 , -55 , -57 , -59 , -61 ,
-63 , -65 , -67 , -69 , -71 , -73 , -75 , -77 , -79 , -81 , -83 , -85 , -87 , -89 , -91 , -93 ,
-95 , -97 , -99 , -101 , -103 , -105 , -107 , -109 , -111 , -113 , -115 , -117 , -119 , -121 , -123 , -125
};
char sawtooth256[256]   __attribute__ ((aligned(256))) = {
-127 , -127 , -126 , -125 , -124 , -123 , -122 , -121 , -120 , -119 , -118 , -117 , -116 , -115 , -114 , -113 ,
-112 , -111 , -110 , -109 , -108 , -107 , -106 , -105 , -104 , -103 , -102 , -101 , -100 , -99 , -98 , -97 ,
-96 , -95 , -94 , -93 , -92 , -91 , -90 , -89 , -88 , -87 , -86 , -85 , -84 , -83 , -82 , -81 ,
-80 , -79 , -78 , -77 , -76 , -75 , -74 , -73 , -72 , -71 , -70 , -69 , -68 , -67 , -66 , -65 ,
-64 , -63 , -62 , -61 , -60 , -59 , -58 , -57 , -56 , -55 , -54 , -53 , -52 , -51 , -50 , -49 ,
-48 , -47 , -46 , -45 , -44 , -43 , -42 , -41 , -40 , -39 , -38 , -37 , -36 , -35 , -34 , -33 ,
-32 , -31 , -30 , -29 , -28 , -27 , -26 , -25 , -24 , -23 , -22 , -21 , -20 , -19 , -18 , -17 ,
-16 , -15 , -14 , -13 , -12 , -11 , -10 , -9 , -8 , -7 , -6 , -5 , -4 , -3 , -2 , -1 ,
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ,
16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 ,
32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 ,
48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 , 62 , 63 ,
64 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , 73 , 74 , 75 , 76 , 77 , 78 , 79 ,
80 , 81 , 82 , 83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 91 , 92 , 93 , 94 , 95 ,
96 , 97 , 98 , 99 , 100 , 101 , 102 , 103 , 104 , 105 , 106 , 107 , 108 , 109 , 110 , 111 ,
112 , 113 , 114 , 115 , 116 , 117 , 118 , 119 , 120 , 121 , 122 , 123 , 124 , 125 , 126 , 127
};



#include "avr/pgmspace.h"



// log table for 128 filter cutoffs
unsigned char logCutoffs[128] = {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x03,0x04,0x04,0x04,0x04,0x04,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x06,0x06,0x06,0x07,0x08,0x08,0x08,0x09,0x09,0x0A,0x0A,0x0A,0x0A,0x0B,0x0C,0x0C,0x0C,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1E,0x20,0x21,0x22,0x23,0x24,0x26,0x28,0x2A,0x2C,0x2E,0x30,0x32,0x34,0x36,0x38,0x3A,0x40,0x42,0x44,0x48,0x4C,0x4F,0x52,0x55,0x58,0x5D,0x61,0x65,0x68,0x6C,0x70,0x76,0x7E,0x85,0x8A,0x90,0x96,0x9D,0xA4,0xAB,0xB0,0xBA,0xC4,0xCE,0xD8,0xE0,0xE8,0xF4,0xFF};

volatile unsigned int WAIT_curTime;
#define WAIT_UNTIL_INTERRUPT()     WAIT_curTime=loopSteps;    while(WAIT_curTime==loopSteps){}


#define SERIAL_OUT 0

// attack,decay are in 1/64ths per 125th of a second - ie. 1 = 0->1 in half a second
const int DECAY=3;
const int ATTACK=4;


volatile char* curWave=square256;

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// this is supposedly the audio clock frequency - as
// you can see, measured freq may vary a bit from supposed clock frequency
// I'm not quite sure why
// const double refclk=31372.549;  // =16MHz / 510
// const double refclk=31376.6;      // measured


// variables used inside interrupt service declared as voilatile
// these variables allow you to keep track of time - as delay / millis etc. are
// made inactive due to interrupts being disabled.
volatile unsigned char loopSteps=0; // once per sample
volatile unsigned int loopStepsHigh=0; // once per 256 samples

// information about the current state of a single oscillator
struct oscillatorPhase
{
   unsigned int phaseStep;
   char volume;
   unsigned int phaseAccu;
};

// the oscillators (8 of them)
struct oscillatorPhase oscillators[8];

//  tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word
// to get hz -> tuning word do: (pow(2,16) * frequency) / 31376.6
const unsigned int NOTE_FREQS[25]={273,289,307,325,344,365,386,409,434,460,487,516,546,579,613,650,688,729,773,819,867,919,974,1032,1093};

// thresholds for the capacitive sensing buttons
int calibrationThresholds[8]={0,0,0,0,0,0,0,0};

inline int getNoteKeys(boolean calibrate=false)
{

  char PORTD_PINS=0b11000000; // (pins 6-7 - avoid pins 0,1 as they are used for serial port comms)
  char PORTC_PINS=0b111111; //(analog pins 0-5) 

  const int MAX_LOOPS=16;
  char port_values[MAX_LOOPS*2];
 
  WAIT_UNTIL_INTERRUPT();
  asm volatile (
  // port D reading loop:
//  DDRD &= ~(PORTD_PINS = 0x3f);          // set pins 8-12 to input mode
    "in %[temp],0x0a" "\n\t"
    "andi %[temp],0x3f" "\n\t"
    "out 0x0a,%[temp]" "\n\t"
//  PORTD |= (PORTD_PINS);          // set pins 8-12 pullup on
    "in %[temp],0x0b" "\n\t"
    "ori %[temp],0xC0" "\n\t"
    "out 0x0b,%[temp]" "\n\t"
    "in %0,0x09" "\n\t"
    "in %1,0x09" "\n\t"
    "in %2,0x09" "\n\t"
    "in %3,0x09" "\n\t"
    "in %4,0x09" "\n\t"
    "in %5,0x09" "\n\t"
    "in %6,0x09" "\n\t"
    "in %7,0x09" "\n\t"
    "in %8,0x09" "\n\t"
    "in %9,0x09" "\n\t"
    "in %10,0x09" "\n\t"
    "in %11,0x09" "\n\t"
    "in %12,0x09" "\n\t"
    "in %13,0x09" "\n\t"
    "in %14,0x09" "\n\t"
    "in %15,0x09" "\n\t"
  :
  // outputs
  "=r" (port_values[0]),
  "=r" (port_values[2]),
  "=r" (port_values[4]),
  "=r" (port_values[6]),
  "=r" (port_values[8]),
  "=r" (port_values[10]),
  "=r" (port_values[12]),
  "=r" (port_values[14]),
  "=r" (port_values[16]),
  "=r" (port_values[18]),
  "=r" (port_values[20]),
  "=r" (port_values[22]),
  "=r" (port_values[24]),
  "=r" (port_values[26]),
  "=r" (port_values[28]),
  "=r" (port_values[30])
  :[temp] "d" (0));

  WAIT_UNTIL_INTERRUPT();
  asm volatile (
  // port C reading loop:
//  DDRC &= ~(PORTC_PINS = 0xc0);          // set pins 5-7 to input mode
    "in %[temp],0x07" "\n\t"
    "andi %[temp],0xc0" "\n\t"
    "out 0x07,%[temp]" "\n\t"
//  PORTC |= (PORTC_PINS);          // set pins 5-7 pullup on
    "in %[temp],0x08" "\n\t"
    "ori %[temp],0x3F" "\n\t"
    "out 0x08,%[temp]" "\n\t"
    "in %0,0x06" "\n\t"
    "in %1,0x06" "\n\t"
    "in %2,0x06" "\n\t"
    "in %3,0x06" "\n\t"
    "in %4,0x06" "\n\t"
    "in %5,0x06" "\n\t"
    "in %6,0x06" "\n\t"
    "in %7,0x06" "\n\t"
    "in %8,0x06" "\n\t"
    "in %9,0x06" "\n\t"
    "in %10,0x06" "\n\t"
    "in %11,0x06" "\n\t"
    "in %12,0x06" "\n\t"
    "in %13,0x06" "\n\t"
    "in %14,0x06" "\n\t"
    "in %15,0x06" "\n\t"
  :
  // outputs
  "=r" (port_values[1]),
  "=r" (port_values[3]),
  "=r" (port_values[5]),
  "=r" (port_values[7]),
  "=r" (port_values[9]),
  "=r" (port_values[11]),
  "=r" (port_values[13]),
  "=r" (port_values[15]),
  "=r" (port_values[17]),
  "=r" (port_values[19]),
  "=r" (port_values[21]),
  "=r" (port_values[23]),
  "=r" (port_values[25]),
  "=r" (port_values[27]),
  "=r" (port_values[29]),
  "=r" (port_values[31]) 
  :[temp] "d" (0));

  PORTC &= ~(PORTC_PINS); // pullup off pins 8-12
  PORTD &= ~(PORTD_PINS); // pullup off pins 5-7
  DDRC |= (PORTC_PINS); // discharge
  DDRD |= (PORTD_PINS); // discharge

  if(calibrate)
  {     
    for(int c=0;c<8;c++)
    {
      for(int d=0;d<MAX_LOOPS;d++)
      {
        int liveNotes=((int*)port_values)[d];
        liveNotes&=0x3fc0;
        liveNotes>>=6;
        if(liveNotes&(1<<c))
        {
          if(calibrationThresholds[c]<=d)
          {
            calibrationThresholds[c]=d+1;
          }
          break;
        }
      }
    }
  }
  int liveNotes=0;
  for(int c=0;c<8;c++)
  {
      int val = ((int*)port_values)[calibrationThresholds[c]+1];
      val&=0x3fc0;
      val>>=6;
      if((val&(1<<c))==0)
      {
        liveNotes|=(1<<c);
      }
  }
  return liveNotes; 
}

// get capacitive touch on input 4 and output 3
// used for filter modulator 
inline int getfiltermodulationtime()
{
    static int running_average=0;
    static int running_min=1024;
    static int running_min_inc_count=0;
    static boolean initialise_running_min=true;

    unsigned int delayTime=0;
    char PINNUM_OUT=3;
    char PINNUM_IN=4;
    char PIN_OUT=1<<PINNUM_OUT;
    char PIN_IN=1<<PINNUM_IN;
    // make sure inputs / outputs are set right
    DDRD|=PIN_OUT;
    DDRD&=~(PIN_IN);
    WAIT_UNTIL_INTERRUPT();
    PORTD|=PIN_OUT;
    asm volatile (
         "loopstart%=:" "\n\t"
         "sbic 0x09,%[PINNUM_IN]" "\n\t"
         "rjmp outloop%=" "\n\t"
         "adiw %[delayTime],0x01" "\n\t"
         "cpi %B[delayTime],0x02" "\n\t"
         "brne loopstart%=" "\n\t"
         "outloop%=:" "\n\t"
         :[delayTime] "+&w" (delayTime)
         :[PINNUM_IN] "I" (PINNUM_IN));
    // set pin down - maybe don't bother timing, if it doesn't seem to add
    // much accuracy?
    WAIT_UNTIL_INTERRUPT();
    PORTD&=~PIN_OUT;
    asm(
         "loopstart%=:" "\n\t"
         "sbis 0x09,%[PINNUM_IN]" "\n\t"
         "rjmp outloop%=" "\n\t"
         "adiw %[delayTime],0x01" "\n\t"
         "cpi %B[delayTime],0x02" "\n\t"
         "brne loopstart%=" "\n\t"
         "outloop%=:" "\n\t"
         :[delayTime] "+&w" (delayTime)
         :[PINNUM_IN] "I" (PINNUM_IN));
    running_average=(running_average-(running_average>>4))+(delayTime>>4);
    running_min_inc_count++;
    if(running_min_inc_count==255)
    {
      if(initialise_running_min)
      {
        running_min=running_average;
        running_min_inc_count=0;
        initialise_running_min=false;
      }else{
        running_min_inc_count=0;
        running_min++;    
      }
    }
    if(running_average<running_min)
    {
      running_min=running_average;
    }
    int touchVal=running_average-running_min;
    if(touchVal>15)
    {
      touchVal-=15;
      if(touchVal>99)
      {
        touchVal=99;
      }
    }else{
      touchVal=0;
    }
    return touchVal;
}


// get capacitive touch on input 5 and output 3
// used for pitch bend
inline int getpitchbendtime()
{
    static int running_average=0;
    static int running_min=1024;
    static int running_min_inc_count=0;
    static boolean initialise_running_min=true;

    unsigned int delayTime=0;
    char PINNUM_OUT=3;
    char PINNUM_IN=5;
    char PIN_OUT=1<<PINNUM_OUT;
    char PIN_IN=1<<PINNUM_IN;
    // make sure inputs / outputs are set right
    DDRD|=PIN_OUT;
    DDRD&=~(PIN_IN);
    WAIT_UNTIL_INTERRUPT();
    PORTD|=PIN_OUT;
    asm volatile (
         "loopstart%=:" "\n\t"
         "sbic 0x09,%[PINNUM_IN]" "\n\t"
         "rjmp outloop%=" "\n\t"
         "adiw %[delayTime],0x01" "\n\t"
         "cpi %B[delayTime],0x02" "\n\t"
         "brne loopstart%=" "\n\t"
         "outloop%=:" "\n\t"
         :[delayTime] "+&w" (delayTime)
         :[PINNUM_IN] "I" (PINNUM_IN));
    // set pin down - maybe don't bother timing, if it doesn't seem to add
    // much accuracy?
    WAIT_UNTIL_INTERRUPT();
    PORTD&=~PIN_OUT;
    asm(
         "loopstart%=:" "\n\t"
         "sbis 0x09,%[PINNUM_IN]" "\n\t"
         "rjmp outloop%=" "\n\t"
         "adiw %[delayTime],0x01" "\n\t"
         "cpi %B[delayTime],0x02" "\n\t"
         "brne loopstart%=" "\n\t"
         "outloop%=:" "\n\t"
         :[delayTime] "+&w" (delayTime)
         :[PINNUM_IN] "I" (PINNUM_IN));
    running_average=(running_average-(running_average>>4))+(delayTime>>4);
    running_min_inc_count++;
    if(running_min_inc_count==255)
    {
      if(initialise_running_min)
      {
        running_min=running_average;
        running_min_inc_count=0;
        initialise_running_min=false;
      }else{
        running_min_inc_count=0;
        running_min++;    
      }
    }
    if(running_average<running_min)
    {
      running_min=running_average;
    }
    int touchVal=running_average-running_min;
    if(touchVal>15)
    {
      touchVal-=15;
      if(touchVal>99)
      {
        touchVal=99;
      }
    }else{
      touchVal=0;
    }
    return touchVal;
}

unsigned int pitchBendTable[201]={241,            241,       241,       242,       242,       242,       242,       242,       242,       242,       243,                243,       243,       243,       243,       243,       243,       244,       244,       244,       244,       244,       244,       244,       245,       245,                245,       245,       245,       245,       245,       246,       246,       246,       246,       246,       246,       246,       247,       247,       247,                247,       247,       247,       247,       248,       248,       248,       248,       248,       248,       248,       249,       249,       249,       249,                249,       249,       249,       250,       250,       250,       250,       250,       250,       250,       251,       251,       251,       251,       251,                251,       251,       252,       252,       252,       252,       252,       252,       253,       253,       253,       253,       253,       253,       253,                254,       254,       254,       254,       254,       254,       254,       255,       255,       255,       255,       255,       255,       256,
256,256,               256,       256,       256,       256,       256,       256,       257,       257,       257,       257,       257,       257,       257,       258,                258,       258,       258,       258,       258,       259,       259,       259,       259,       259,       259,       259,       260,       260,       260,                260,       260,       260,       260,       261,       261,       261,       261,       261,       261,       262,       262,       262,       262,       262,                262,       262,       263,       263,       263,       263,       263,       263,       264,       264,       264,       264,       264,       264,       264,                265,       265,       265,       265,       265,       265,       266,       266,       266,       266,       266,       266,       266,       267,       267,                267,       267,       267,       267,       268,       268,       268,       268,       268,       268,       269,       269,       269,       269,       269,                269,       269,       270,       270,       270,       270,       270,       270,       271,       271};

void setupNoteFrequencies(int baseNote,int pitchBendVal /*-100 -> 100*/)
{
  oscillators[0].phaseStep=NOTE_FREQS[baseNote];
  oscillators[1].phaseStep=NOTE_FREQS[baseNote+2];
  oscillators[2].phaseStep=NOTE_FREQS[baseNote+4];
  oscillators[3].phaseStep=NOTE_FREQS[baseNote+5];
  oscillators[4].phaseStep=NOTE_FREQS[baseNote+7];
  oscillators[5].phaseStep=NOTE_FREQS[baseNote+9];
  oscillators[6].phaseStep=NOTE_FREQS[baseNote+11];
  oscillators[7].phaseStep=NOTE_FREQS[baseNote+12];

  if(pitchBendVal<-99)
  {
     pitchBendVal=-99;
  }else if(pitchBendVal>99)
  {
    pitchBendVal=99;
  }
//  Serial.print("*");
//  Serial.print(pitchBendVal);
  unsigned int pitchBendMultiplier=pitchBendTable[pitchBendVal+100];
//  Serial.print(":");
//  Serial.print(pitchBendMultiplier);
  for(int c=0;c<8;c++)
  {
    // multiply 2 16 bit numbers together and shift 8 without precision loss
    // requires assembler really
    volatile unsigned char zeroReg=0;
    volatile unsigned int multipliedCounter=oscillators[c].phaseStep;
    asm volatile
    (
      // high bytes mult together = high  byte
      "ldi %A[outVal],0" "\n\t"
      "mul %B[phaseStep],%B[pitchBend]" "\n\t"
      "mov %B[outVal],r0" "\n\t"
      // ignore overflow into r1 (should never overflow)
      // low byte * high byte -> both bytes
      "mul %A[phaseStep],%B[pitchBend]" "\n\t"
      "add %A[outVal],r0" "\n\t"
      // carry into high byte
      "adc %B[outVal],r1" "\n\t"
      // high byte* low byte -> both bytes
      "mul %B[phaseStep],%A[pitchBend]" "\n\t"
      "add %A[outVal],r0" "\n\t"
      // carry into high byte
      "adc %B[outVal],r1" "\n\t"
      // low byte * low byte -> round
      "mul %A[phaseStep],%A[pitchBend]" "\n\t"
      // the adc below is to round up based on high bit of low*low:
      "adc %A[outVal],r1" "\n\t"
      "adc %B[outVal],%[ZERO]" "\n\t"
      "clr r1" "\n\t"
      :[outVal] "=&d" (multipliedCounter)
      :[phaseStep] "d" (oscillators[c].phaseStep),[pitchBend] "d"( pitchBendMultiplier),[ZERO] "d" (zeroReg)
      :"r1","r0"
      );
    oscillators[c].phaseStep=multipliedCounter;
  }
//  Serial.print(":");
//  Serial.print(NOTE_FREQS[baseNote]);
//  Serial.print(":");
//  Serial.println(oscillators[0].phaseStep);
 
}

void setup()
{
  Serial.begin(9600);        // connect to the serial port
  #ifndef FILTER_LPF_NONE
    setFilter(127, 0);
  #endif

  pinMode(11, OUTPUT);     // pin11= PWM  output / frequency output

  setupNoteFrequencies(12,0);

  for(int c=0;c<8;c++)
  {
    oscillators[c].volume=0;
  }

  Setup_timer2();

  // disable interrupts to avoid timing distortion
  cbi (TIMSK0,TOIE0);              // disable Timer0 !!! delay() is now not available
  sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt


  // calibrate the unpressed key values

  for(int x=0;x<1024;x++)
  {
    getNoteKeys(true);
    int steps=loopSteps;
    WAIT_UNTIL_INTERRUPT();
//    int afterSteps=loopSteps;
//    Serial.println(afterSteps-steps);
  }
  // test pattern intro
#ifdef TEST_PATTERN_INTRO
  int filtValue=255;
  byte notes[]={0x1,0x4,0x10,0x80,0x80,0x80,0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1,0x1,0x1,0x00,0x00,0x1,0x1,0x1,0x1,0x00,0x00,0x5,0x5,0x5,0x5,0x00,0x00,0x15,0x15,0x15,0x15,0x15,0x00,0x00,0x95,0x95,0x95,0x95,0x95,0x95,0x00};
  for(int note=0;note<sizeof(notes)/sizeof(byte);note++)
  {

    int noteCount=0;
    for(int c=0;c<8;c++)
    {
      if(notes[note]&(1<<c))
      {
        noteCount+=1;
      }
    }
    for(int c=0;c<8;c++)
    {
      if(notes[note]&(1<<c))
      {
           oscillators[c].volume=63/noteCount;
      }else
      {
           oscillators[c].volume=0;
      }
    }
    for(int c=0;c<50;c++)
    {
      // might as well keep calibrating here
      // nb: each calibration loop = at least 1 interrupt
     
      getNoteKeys(true);
      #ifndef FILTER_LPF_NONE
        setFilter(127-c, 64);
      #endif
     
    }
  }
#else
  // just beep to show calibration is done
    oscillators[0].volume=63;
    for(int c=0;c<20;c++)
    {
      WAIT_UNTIL_INTERRUPT();
    }
    oscillators[0].volume=63;
 
#endif
  Serial.println("Calibrations:");
  for(int c=0;c<8;c++)
  {
    Serial.print(c);
    Serial.print(":");
    Serial.println(calibrationThresholds[c]);
  }

}


void loop()
{
  // we keep a list of 'raw' volumes - and turn down the volume if a chord is taking >64 volume total
  // this is to allow chording without reducing the volume of single notes
  int rawVolumes[8]={0,0,0,0,0,0,0,0};
  int curNote=0;
  unsigned int filterSweep=64;
  const int MIN_SWEEP=64;
  const int MAX_SWEEP=127;
  const int SWEEP_SPEED=3;
  int sweepDir=SWEEP_SPEED;
  unsigned int lastStep=loopStepsHigh;
  unsigned curStep=loopStepsHigh;
  while(1)
  {
    lastStep=curStep;
    curStep=loopStepsHigh;
    // NOTE: timers do not work in this code (interrupts disabled / speeds changed), so don't even think about calling: delay(), millis / micros etc.
    // each loopstep is roughly 31250 / second
    // this main loop will get called once every 3 or 4 samples if the serial output is turned off, maybe slower otherwise
   
    int liveNotes=getNoteKeys();
    // we are right after an interrupt (as loopStep has just been incremented)
    // so we should have enough time to do the capacitative key checks

    if(lastStep!=curStep)
    {
      int totalVolume=0;
      for(int c=0;c<8;c++)
      {
        if((liveNotes&(1<<c))==0)
        {
          rawVolumes[c]-=DECAY*(curStep-lastStep);
          if(rawVolumes[c]<0)rawVolumes[c]=0;
          if(SERIAL_OUT)Serial.print(".");
        }
        else
        {
            rawVolumes[c]+=ATTACK*(curStep-lastStep);
            if(rawVolumes[c]>63)rawVolumes[c]=63;
            if(SERIAL_OUT)Serial.print(c);
        }
        totalVolume+=rawVolumes[c];
      }
      WAIT_UNTIL_INTERRUPT();
      if( totalVolume<64 )
      {
        for(int c=0;c<8;c++)
        {
          oscillators[c].volume=rawVolumes[c];
        }
      }else
      {
        // total volume too much, scale down to avoid clipping
        for(int c=0;c<8;c++)
        {
          oscillators[c].volume=(rawVolumes[c]*63)/totalVolume;
        }
      }
    }
    if(SERIAL_OUT)Serial.println("");
  #ifndef FILTER_LPF_NONE
/*    if(liveNotes==0)
    {
       filterSweep=64;
       sweepDir=SWEEP_SPEED;
    }
    filterSweep+=sweepDir;
    if(filterSweep>=MAX_SWEEP)
    {
      filterSweep=MAX_SWEEP;
      sweepDir=-sweepDir;
    }
    else if (filterSweep<=MIN_SWEEP)
    {
      sweepDir=-sweepDir;
      filterSweep=MIN_SWEEP;
    }*/
//    Serial.println((int)filterValue);
//    filterSweep=127-(getpitchbendtime()>>1);
    WAIT_UNTIL_INTERRUPT();
    setFilter(150-(getfiltermodulationtime()),220);
    #endif
    WAIT_UNTIL_INTERRUPT();
    setupNoteFrequencies(12,-getpitchbendtime());
    
    // we are right after an interrupt again (as loopStep has just been incremented)
    // so we should have enough time to check the pitch bend capacitance without going over another sample, timing is quite important here
    // need to balance using a big enough resistor to get decent sensing distance with taking too long to sample
    // check the pitch bend input
   
  }
 }
//******************************************************************
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer2() {

// Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode set to Phase Correct PWM
  cbi (TCCR2A, COM2A0);  // clear Compare Match
  sbi (TCCR2A, COM2A1);

  sbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}

#ifdef FILTER_LPF_BIQUAD
char filtValueA1=0,filtValueA2=0,filtValueA3=0,filtValueB1=0,filtValueB2=0;
volatile unsigned char filtCoeffA1=255;
volatile char filtCoeffB1=127;
volatile unsigned char filtCoeffB2=255;
#endif
#ifdef FILTER_LPF_HACK
// hacked low pass filter - 2 pole resonant -
// a += f*((in-a)+ q*(a-b)
// b+= f* (a-b)
int filterA=0;
int filterB=0;
unsigned char filterQ=0;
unsigned char filterF=255;

inline void setFilterRaw(unsigned char filterF, unsigned char resonance)
{
  unsigned char tempReg=0,tempReg2=0;
  asm volatile("ldi %[tempReg], 0xff" "\n\t"
      "sub %[tempReg],%[filtF] " "\n\t"
      "lsr %[tempReg]" "\n\t"
      "ldi %[tempReg2],0x04" "\n\t"
      "add %[tempReg],%[tempReg2]" "\n\t"
      "sub %[reso],%[tempReg]" "\n\t"
      "brcc Res_Overflow%=" "\n\t"
      "ldi %[reso],0x00" "\n\t"
      "Res_Overflow%=:" "\n\t"
      "mov %[filtQ],%[reso]" "\n\t"
  :[tempReg] "=&d" (tempReg),[tempReg2] "=&d" (tempReg2),[filtQ] "=&d" (filterQ): [reso] "d" (resonance), [filtF] "d" (filterF) );
}

inline void setFilter(unsigned char f, unsigned char resonance)
{
  if(f>127)f=127;
  filterF=logCutoffs[f];
  setFilterRaw(filterF,resonance);
}
#endif



#define HIBYTE(__x) ((char)(((unsigned int)__x)>>8))
#define LOBYTE(__x) ((char)(((unsigned int)__x)&0xff))

// oscillator main loop (increment wavetable pointer, and add it to the output registers)
// 13 instructions - should take 14 processor cycles according to the datasheet
// in theory I think this means that each oscillator should take 1.5% of cpu
// (plus a constant overhead for interrupt calls etc.)
// Note: this used to do all the stepvolume loads near the start, but they are now interleaved in the
// code, this is because ldd (load with offset) takes 2 instructions,
// versus ld,+1 (load with post increment) and st,+1 which are 1 instruction - we can do this because:
//
// a)the step (which doesn't need to be stored back) is in memory before the
//   phase accumulator (which does need to be stored back once the step is added
//
// b)the phase assumulator is stored in low byte, high byte order, meaning that we
//   can add the first bytes together, then store that byte incrementing the pointer,
//   then load the high byte, add the high bytes together and store incrementing the pointer
//  
// I think this is the minimum number of operations possible to code this oscillator in, because
// 1)There are 6 load operations required (to load stepHigh/Low,phaseH/L,volume, and the value from the wave)
// 2)There are 2 add operations required to add to the phase accumulator
// 3)There are 2 store operations required to save the phase accumulator
// 4)There is 1 multiply (2 instruction cycles) required to do the volume
// 5)There are 2 add operations required to add to the final output
//
// 6+2+2+2+2 = 14 instruction cycles

#define OSCILLATOR_ASM \
    /* load phase step and volume*/ \
    "ld %A[tempStep],%a[stepVolume]+" "\n\t" \
    "ld %B[tempStep],%a[stepVolume]+" "\n\t" \
    "ld %[tempVolume],%a[stepVolume]+" "\n\t" \
    /* load phase accumulator - high byte goes straight*/ \
    /* into the wave lookup array (wave is on 256 byte boundary*/ \
    /* so we can do this without any adds */ \
    /* Do the phase adds in between the two loads, as load with offset is slower than
       just a normal load
    */\
    "ld %A[tempPhaseLow],%a[stepVolume]" "\n\t" \
    /* add phase step low */ \
    "add %[tempPhaseLow],%A[tempStep]" "\n\t"\
    /* store phase accumulator low */ \
    "st %a[stepVolume]+,%[tempPhaseLow]" "\n\t" \
    /* load phase accumulator high*/\
    "ld %A[waveBase],%a[stepVolume]" "\n\t" \
    /* add phase step high - with carry from the add above */\
    "adc %A[waveBase],%B[tempStep]" "\n\t"\
    /* store phase step high */\
    "st %a[stepVolume]+,%A[waveBase]" "\n\t" \
    /* now lookup from the wave - high byte = wave pointer, low byte=offset*/ \
    "ld %[tempPhaseLow],%a[waveBase]" "\n\t" \
    /* now multiply by volume*/ \
    "muls %[tempPhaseLow],%[tempVolume]" "\n\t" \
    /* r0 now contains a sample - add it to output value*/ \
    "add %A[outValue],r0" "\n\t" \
    "adc %B[outValue],r1" "\n\t" \
    /* go to next oscillator - stepVolume is pointing at next*/ \
    /* oscillator already */  \
   


//******************************************************************
// Timer2 Interrupt Service at 31372,550 KHz = 32uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : ?
ISR(TIMER2_OVF_vect) {


    // now set up the next value
    // this loop takes roughly 172 cycles (214 including the push/pops) - we have 510, so roughly 50% of the processor going spare for non-audio tasks
    // the low pass filter also takes some cycles

    int outValue;

    // pointers:
    // X = oscillator phase accumulator
    // Y = oscillator step and volume
    // Z = wave pos - needs to add to base
    int tempStep=0;
    char tempPhaseLow=0,tempVolume=0;
    int tempWaveBase=0;
    asm  volatile (
    "ldi %A[outValue],0" "\n\t"
    "ldi %B[outValue],0" "\n\t"
    // oscillator 0
    // uncomment the code below to check
    // that registers aren't getting double assigned
/*    "lds %A[outValue],0x00" "\n\t"
    "lds %B[outValue],0x01" "\n\t"
    "lds %A[tempPhaseLow],0x02" "\n\t"
//    "lds %B[tempPhase],0x03" "\n\t"
    "lds %A[tempStep],0x04" "\n\t"
    "lds %B[tempStep],0x05" "\n\t"
    "lds %[tempVolume],0x06" "\n\t"
    "lds %[ZERO],0x07" "\n\t"
    "lds %A[tempWaveBase],0x08" "\n\t"
    "lds %B[tempWaveBase],0x09" "\n\t"
    "lds %A[phaseAccu],0x0a" "\n\t"
    "lds %B[phaseAccu],0x0b" "\n\t"
    "lds %A[stepVolume],0x0c" "\n\t"
    "lds %B[stepVolume],0x0d" "\n\t"   
    "lds %A[waveBase],0x0e" "\n\t"
    "lds %B[waveBase],0x0f" "\n\t"*/
    OSCILLATOR_ASM
    OSCILLATOR_ASM
    OSCILLATOR_ASM
    OSCILLATOR_ASM
    OSCILLATOR_ASM
    OSCILLATOR_ASM
    OSCILLATOR_ASM
    OSCILLATOR_ASM
    :
    // outputs
    [tempPhaseLow] "=&d" (tempPhaseLow),
    [tempStep] "=&d" (tempStep),
    [tempVolume] "=&d" (tempVolume),
    [outValue] "=&d" (outValue)
    :
    // inputs
    [stepVolume] "y" (&oscillators[0].phaseStep),
    [waveBase] "z" (256*(((unsigned int)curWave)>>8))
    :
    // other registers we clobber (by doing multiplications)
    "r1"   
    );
   
    // at this point outValue = oscillator value
    // it is currently maxed to full volume / 4
    // to allow some headroom for filtering
   
   
#ifdef FILTER_LPF_HACK

// a low pass filter based on the one from MeeBlip (http://meeblip.noisepages.com)
// a += f*((in-a)+ q*(a-b)
// b+= f* (a-b)
//  outValue>>=3;
// started at 4700
// 4686
  int tempReg,tempReg2=0;
  unsigned char zeroRegFilt=0;
  // de-volatilisati
  unsigned char filtF=filterF;
  unsigned char filtQ=filterQ;
  asm volatile(
      "sub %A[outVal],%A[filtA]" "\n\t"
      "sbc %B[outVal],%B[filtA]" "\n\t"
      "brvc No_overflow1%=" "\n\t"
      "ldi %A[outVal],0b00000001" "\n\t"
      "ldi %B[outVal],0b10000000" "\n\t"
      "No_overflow1%=:" "\n\t"    
      // outVal = (in - filtA)
      "mov %A[tempReg],%A[filtA]" "\n\t"
      "mov %B[tempReg],%B[filtA]" "\n\t"
      "sub %A[tempReg],%A[filtB]" "\n\t"
      "sbc %B[tempReg],%B[filtB]" "\n\t"
      "brvc No_overflow3%=" "\n\t"
      "ldi %A[tempReg],0b00000001" "\n\t"
      "ldi %B[tempReg],0b10000000" "\n\t"
      "No_overflow3%=:" "\n\t"    
      // tempReg = (a-b)
      "mulsu %B[tempReg],%[filtQ]" "\n\t"
      "movw %A[tempReg2],r0" "\n\t"
      // tempReg2 = (HIBYTE(a-b))*Q
      "mul %A[tempReg],%[filtQ]" "\n\t"
      "add %A[tempReg2],r1" "\n\t"
      "adc %B[tempReg2],%[ZERO]" "\n\t"
      "rol r0" "\n\t"
      "brcc No_Round1%=" "\n\t"
      "inc %A[tempReg2]" "\n\t"
      "No_Round1%=:" "\n\t"    
      // at this point tempReg2 = (a-b)*Q (shifted appropriately and rounded)
//      "clc" "\n\t"
      "lsl %A[tempReg2]" "\n\t"
      "rol %B[tempReg2]" "\n\t"
 //     "clc" "\n\t"
      "lsl %A[tempReg2]" "\n\t"
      "rol %B[tempReg2]" "\n\t"
      // tempReg2 = (a-b)*Q*4
      "add %A[outVal],%A[tempReg2]" "\n\t"
      "adc %B[outVal],%B[tempReg2]" "\n\t"
      "brvc No_overflow4%=" "\n\t"
      "ldi %A[outVal],0b11111111" "\n\t"
      "ldi %B[outVal],0b01111111" "\n\t"
      "No_overflow4%=:" "\n\t"    
      // outVal = ((in-a) + (a-b)*(Q>>8)*4) - clipped etc
      "mulsu %B[outVal],%[filtF]" "\n\t"
      "movw %A[tempReg],r0" "\n\t"
      "mul %A[outVal],%[filtF]" "\n\t"
      "add %A[tempReg],r1" "\n\t"
      "adc %B[tempReg],%[ZERO]" "\n\t"
      "rol r0" "\n\t"
      "brcc No_Round2%=" "\n\t"
      "inc %A[tempReg]" "\n\t"
      // tempReg = f* ((in-a) + (a-b)*(Q>>8)*4)
      "No_Round2%=:" "\n\t"
      "add %A[filtA],%A[tempReg]" "\n\t"
      "adc %B[filtA],%B[tempReg]" "\n\t"
      // A= A+ f* ((in-a) + (a-b)*(Q>>8)*4)
      "brvc No_overflow5%=" "\n\t"
      "ldi %A[outVal],0b11111111" "\n\t"
      "ldi %B[outVal],0b01111111" "\n\t"
      "No_overflow5%=:" "\n\t"    
      // now calculate B= f* (a - b)

      "mov %A[tempReg],%A[filtA]" "\n\t"
      "mov %B[tempReg],%B[filtA]" "\n\t"
      "sub %A[tempReg],%A[filtB]" "\n\t"
      "sbc %B[tempReg],%B[filtB]" "\n\t"
      "brvc No_overflow6%=" "\n\t"
      "ldi %A[tempReg],0b00000001" "\n\t"
      "ldi %B[tempReg],0b10000000" "\n\t"
      "No_overflow6%=:" "\n\t"
      // tempReg = (a-b)
      "mulsu %B[tempReg],%[filtF]" "\n\t"
      "movw %A[tempReg2],r0" "\n\t"
      "mul %A[tempReg],%[filtF]" "\n\t"
      "add %A[tempReg2],r1" "\n\t"
      "adc %B[tempReg2],%[ZERO]" "\n\t"
      // tempReg2 = f*(a-b)
      "add %A[filtB],%A[tempReg2]" "\n\t"
      "adc %B[filtB],%B[tempReg2]" "\n\t"
      "brvc No_overflow7%=" "\n\t"
      "ldi %A[filtB],0b11111111" "\n\t"
      "ldi %B[filtB],0b01111111" "\n\t"
      "No_overflow7%=:" "\n\t"    
      // now b= b+f*(a-b)
      "mov %A[outVal],%A[filtB]" "\n\t"    
      "mov %B[outVal],%B[filtB]" "\n\t"
     
      // multiply outval by 4 and clip
      "add %A[outVal],%A[filtB]" "\n\t"
      "adc %B[outVal],%B[filtB]" "\n\t"
      "brbs 3, Overflow_End%=" "\n\t"
     
      "add %A[outVal],%A[filtB]" "\n\t"
      "adc %B[outVal],%B[filtB]" "\n\t"
      "brbs 3, Overflow_End%=" "\n\t"

      "add %A[outVal],%A[filtB]" "\n\t"
      "adc %B[outVal],%B[filtB]" "\n\t"
      "brbs 3, Overflow_End%=" "\n\t"
      "rjmp No_overflow%=" "\n\t"
      "Overflow_End%=:" "\n\t"
      "brbs 2,Overflow_High%=" "\n\t"
      "ldi %A[outVal],0b00000001" "\n\t"
      "ldi %B[outVal],0b10000000" "\n\t"
      "rjmp No_overflow%=" "\n\t"
      "Overflow_High%=:" "\n\t"
      "ldi %A[outVal],0b11111111" "\n\t"
      "ldi %B[outVal],0b01111111" "\n\t"
      "No_overflow%=:" "\n\t"
      //char valOut=((unsigned int)(outValue))>>8;
      //valOut+=128;
      // OCR2A=(byte)valOut;
      "subi %B[outVal],0x80" "\n\t"
      "sts 0x00b3,%B[outVal]" "\n\t"
      // uncomment the lines below to see the register allocations
      /*
      "lds %A[filtA],0x01" "\n\t"
      "lds %B[filtA],0x02" "\n\t"
      "lds %A[filtB],0x03" "\n\t"
      "lds %B[filtB],0x04" "\n\t"
      "lds %[filtQ],0x05" "\n\t"
      "lds %[filtF],0x06" "\n\t"
      "lds %A[outVal],0x07" "\n\t"
      "lds %B[outVal],0x08" "\n\t"
      "lds %A[tempReg],0x09" "\n\t"
      "lds %B[tempReg],0x0a" "\n\t"
      "lds %A[tempReg2],0x0b" "\n\t"
      "lds %B[tempReg2],0x0c" "\n\t"
      "lds %[ZERO],0x0d" "\n\t"*/
  :
  // outputs / read/write arguments
[filtA] "+&w" (filterA),
[filtB] "+&w" (filterB),
[tempReg] "=&a" (tempReg),
[tempReg2] "=&d" (tempReg2)
  :
[filtQ] "a" (filtQ),
[filtF] "a" (filtF),
    [outVal] "a" (outValue),
[ZERO] "d" (zeroRegFilt)
  // inputs
  : "r1");


#endif
   
// output is done in the filter assembler code if filters are on   
// otherwise we output it by hand here
#ifdef FILTER_LPF_NONE   
    // full gain    
    outValue*=4;
    // at this point, outValue is a 16 bit signed version of what we want ie. 0 -> 32767, then -32768 -> -1 (0xffff)
    // we want 0->32767 to go to 32768-65535 and -32768 -> -1 to go to 0-32767, then we want only the top byte
    // take top byte, then add 128, then cast to unsigned. The (unsigned int) in the below is to avoid having to shift (ie.just takes top byte)    
    char valOut=((unsigned int)(outValue))>>8;
    valOut+=128;
    OCR2A=(byte)valOut;
#endif    
    // increment loop step counter (and high counter)
    // these are used because we stop the timer
    // interrupt running, so have no other way to tell time
    // this asm is probably not really needed, but it does save about 10 instructions
    // because the variables have to be volatile
    asm(
        "inc %[loopSteps]" "\n\t"
        "brbc 1,loopend%=" "\n\t"
        "inc %A[loopStepsHigh]" "\n\t"
        "brbc 1,loopend%=" "\n\t"
        "inc %B[loopStepsHigh]" "\n\t"
        "loopend%=:" "\n\t"
          :[loopSteps] "+a" (loopSteps),[loopStepsHigh] "+a" (loopStepsHigh):);   
}

Hiç yorum yok:

Yorum Gönder