/* Arduino Perfect Tuning Servoguitar Program * *dual motor control that listens to dedicated pickups and corrects tuning in between note changes *works with midi decoder on second Arduino *this version uses an analog 12 level com link with the midi decoder * string 0 g-f# (0.13) * string 1 c-b (.010) * * * Background Info: * ADC 8-Bit Mode * Analog input 0 (ADC0) is used to sample the audio signal * Timer2 drives interrupt and sample rate (~15 kHz) * this program mainlines avr-gcc on which Arduino IDE is based--but still compiles with Arduino IDE * the ISR set-up is based on code by * KHM 2008 / Lab3/ Martin Nawrath nawrath@khm.de * Kunsthochschule fuer Medien Koeln * Academy of Media Arts Cologne--thanks! */ //*******Defines/Includes //!!deprecated, consider replacing #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #include //the eeprom library //*******Function prototypes int autocorrelate0 (); //does low-budget autocorrelation for motor 0 int autocorrelate1 (); //does low-budget autocorrelation for void savekeymap(); //saves keymap (tunings) in EEPROM void loadkeymap(); //loads keymap (tunings) from EEPROM //void loadhysteresismap(); //loads learning from EEPROM //void savehysteresismap(); //saves learning in EEPROM void analogport0read(); //read the note for channel 0 from the midi decoder board void analogport1read(); //read the note for channel 1 from the midi decoder board //*******Global Variables //pin assignments for hex input int r0=0; int r1=1; int r2=2; int r3=8; int r4=4; int r5=5; int motor0=3; //motor output pins int motor1=11; int led0=12; int led1=13; int chan0=5; int chan1=4; //protect motor variables long startup=0; //toggle switches int plusPin=7; int minusPin=6; int refreshPin=9; //mini timeshare os variables int timeslice=0; //controls time sharing amonng realtime tasks from 0-7 int skipafew=0; // spaces some timeconsuming tasks apart int keynumber=5; //midi note from 0-11 int tuningchannel=0; //used for deciding which string is being tuned int octavenumber=0; //tempvariable for analog port //manual tuning variables int tuneup=0; int debounce=0; int toggle=0; //STRING 0 //keyboard (midi) monitoring variables int pbutton0=5; //the key button (0-11) pressed int previousbutton0 = 0; //variables to detect change in pressed button int lastbutton0 = 0; int steadyerror0=0; int movementtimeout0=0; //slide variables float slidecommand0; float slideincrement0; int slidetimer0=0; int lastcommand0=5; // arrays for tensions, accumulated tuning corrections, and hysteresis byte keymap0[] = { 50, 60, 65, 70, 90, 110, 130, 150, 170, 190, 210, 230, 240}; //initial values before string tuning byte tunecorrectionmap0[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //byte hysteresismap[145]; //errors caused by predictable hysteresis //int indx = 0; //index for hysteresis map //MOTOR 0 //motor 0 control varibles int command0 = 50; // tension value from keymap as indexed by keynumber int collectedcommand0=0; //command plus tuning correction and hysteresis correction int errorsignal0; // voltage to the motor //microcorrection variables byte do_once0=HIGH; int lastsensor_in0=0; //used to detect motor speed int stopcounter0=0; //how long you have stopped int stoptimeout0=0; //timer for stopping even if not close byte motorhasstopped0=LOW; long holdoff0 = -2000; //delay after motor stops for ramping gain and beginning autocorrellation int sharp_flat0 = 0; //output of autocorrelation int correctioncounter0 = 0; //integrator for tuning errors int tunecorrection0 = 0; //fractional tuning adjustment (saved between note changes) int perfectcounter0=0; // autocorrrelation lags for string 0 /*********************************************************** * for scale from G (196 Hz) to F# (370 Hz) * actual periods (perfect lags) for these notes will be: * 79.71, 75.22, 71.02, 67.03, 63.28, 59.72, 56.36, 53.20, 50.22, 47.40, 44.74, 42.22 * of course, only integer lags are possible. *********************************************************** */ float centerlag0[] = { 80, 75, 71, 67, 63, 60, 56, 53, 50, 47.5, 45, 42.5, 30 //45 is F 60 was 59 c }; int centerlag; //STRING 1 //keyboard (midi) monitoring variables int pbutton1=5; //the key button (0-11) pressed int previousbutton1 = 0; //variables to detect change in pressed button int lastbutton1 = 0; int steadyerror1=0; int movementtimeout1=0; //slide variables float slidecommand1; float slideincrement1; int slidetimer1=0; int lastcommand1=5; // arrays for tensions, accumulated tuning corrections, and hysteresis byte keymap1[] = { 50, 60, 65, 70, 90, 110, 130, 150, 170, 190, 210, 230, 235}; byte tunecorrectionmap1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // control varibles int command1 = 50; //128; // tension value from keymap as indexed by keynumber int collectedcommand1=0; //command plus tuning correction and hysteresis correction int errorsignal1; // voltage to the motor //microcorrection variables byte do_once1=HIGH; int lastsensor_in1=0; //used to detect motor speed int stopcounter1=0; int stoptimeout1=0; byte motorhasstopped1=LOW; long holdoff1 = -2000; //delay after motor stops for ramping gain and beginning autocorrellation int sharp_flat1 = 0; //output of autocorrelation int correctioncounter1 = 0; //integrator for tuning errors int tunecorrection1 = 0; //fractional tuning adjustment (saved between note changes) int perfectcounter1=0; int nop=20; // autocorrrelation lags for string 1 /*********************************************************** * for scale from c (261.6 Hz) to B (493.8 Hz) * actual periods (perfect lags) for these notes will be: (actually 1/4 of these--higher string) * 119.45, 112.75, 106.41, 100.44, 94.79, 89.48 , 84.45, 79.73, 75.76, 71.02, 67.03, 63.29 * of course, only integer lags are possible. *********************************************************** */ int centerlag1[] = { 59, 56, 53, 50, 47, 44, 42, 39, 37, 35, 33, 31, 0 }; //AUTOCORRELATION subroutine variables--global for speed int pitchcorrection=0; //returned value of sharp (+1) or flat (-1) or no data (3) int flatvalue=0; //correlation value for a flat note int perfectvalue=0; int sharpvalue=0; int peakvalue=0; //detect non-zero amplitude to trigger autocorrellation int troughvalue=255; float averageflatvalue0=0; //average autocorrellation over many cycles float averageperfectvalue0=0; float averagesharpvalue0=0; float averageflatvalue1=0; //average autocorrellation over many cycles float averageperfectvalue1=0; float averagesharpvalue1=0; int motor0pin = 10; //pin 10 is motor output int motor1pin = 11; //pin 10 is motor output //int pin12 = 12; //pin 12 provides profiling etc // interrupt variables accessed globally volatile boolean sampling=false; //turns on acquire audio buffer volatile boolean servosense=true; //turns on acquire tension sensor data volatile boolean midisense=false; //activates sensing of analog port for midi communication volatile boolean half1; //frequency divider variables volatile boolean half2; volatile boolean half3; volatile int audio_in; //not used--for testing volatile byte mode_in; //mode button value (save data etc) volatile byte sensor_in0; //potentiometer value volatile byte sensor_in1; volatile byte dummy; //is this needed? volatile byte audio_buffer[512]; // Audio Memory Array 8-Bit volatile byte midiin; volatile int buffer_index; void loadkeymap() { if (int(EEPROM.read (5))<255) //check to make sure EEPROM has previous data { for (int i = 0; i < 13; i++) //loop through array in EEPROM keymap0[i] = byte(EEPROM.read (i)); for (int i = 0; i < 13; i++) //loop through array in EEPROM keymap1[i] = byte(EEPROM.read (i+12)); } } void savekeymap() { for (int i = 0; i < 13; i++) //loop through array in EEPROM EEPROM.write(i, byte(keymap0[i])); for (int i = 0; i < 13; i++) //loop through array in EEPROM EEPROM.write(i+12, byte(keymap1[i])); int pause=0; } void analogport0read() //read channel from midi decoder arduino: { octavenumber=0; //set up values for ISR (analogWrite() functions are not working becuase of clock changes servosense=false; midisense=true; buffer_index=0; sbi (ADMUX, MUX0); // Set Input Multiplexer to Channel 5 for midi channel 1 cbi (ADMUX, MUX1); // MUX0-3 select one of 8 A/D inputs Mux0=LSMB sbi (ADMUX, MUX2); cbi (ADMUX, MUX3); sampling=true; //enable sampling in ISR while (sampling==true); //spin until acquistion is complete //set back to normal sensor sampling for motor control midisense=false; half3=true; //reset to tension sampling sbi (ADMUX, MUX0); // Set Input Multiplexer to Channel 0 for tension sensor 0 cbi (ADMUX, MUX1); // MUX0-3 select one of 8 A/D inputs Mux0=LSMB cbi (ADMUX, MUX2); cbi (ADMUX, MUX3); servosense=true; if (midiin < 11) octavenumber=0; else if (midiin < 34) octavenumber=1; else if (midiin < 57) octavenumber=2; else if (midiin < 80) octavenumber=3; else if (midiin < 103) octavenumber=4; else if (midiin < 126) octavenumber=5; else if (midiin < 149) octavenumber=6; else if (midiin< 172) octavenumber=7; else if (midiin < 195) octavenumber=8; else if (midiin < 218) octavenumber=9; else if (midiin < 241) octavenumber=10; else octavenumber=11; pbutton0=octavenumber+9; //some incredible bug with modulo operator so this is a work around while (pbutton0>=0) pbutton0=pbutton0-12; pbutton0=pbutton0+12; return; } void analogport1read() //read channel from midi decoder arduino: { octavenumber=0; //set up values for ISR (analogWrite() functions are not working becuase of clock changes servosense=false; midisense=true; buffer_index=0; cbi (ADMUX, MUX0); // Set Input Multiplexer to Channel 5 for midi channel 1 cbi (ADMUX, MUX1); // MUX0-3 select one of 8 A/D inputs Mux0=LSMB sbi (ADMUX, MUX2); cbi (ADMUX, MUX3); sampling=true; //enable sampling in ISR while (sampling==true); //spin until acquistion is complete //set back to normal sensor sampling for motor control midisense=false; half3=true; //reset to tension sampling sbi (ADMUX, MUX0); // Set Input Multiplexer to Channel 0 for tension sensor 0 cbi (ADMUX, MUX1); // MUX0-3 select one of 8 A/D inputs Mux0=LSMB cbi (ADMUX, MUX2); cbi (ADMUX, MUX3); servosense=true; if (midiin < 11) octavenumber=0; else if (midiin < 34) octavenumber=1; else if (midiin < 57) octavenumber=2; else if (midiin < 80) octavenumber=3; else if (midiin < 103) octavenumber=4; else if (midiin < 126) octavenumber=5; else if (midiin < 149) octavenumber=6; else if (midiin< 172) octavenumber=7; else if (midiin < 195) octavenumber=8; else if (midiin < 218) octavenumber=9; else if (midiin < 241) octavenumber=10; else octavenumber=11; pbutton1=(octavenumber+4)%12; return; } int autocorrelate0 () //listen to signal to determine final correction { flatvalue=0; sharpvalue=0; perfectvalue=0; peakvalue=0; troughvalue=255; //perform pseudocorrellation (uses subtraction not multiplication) if (centerlag0[pbutton0]==int(centerlag0[pbutton0])) { centerlag=(int)centerlag0[pbutton0]; for (int i = 1; i < 200; i++) // was 400=buffer-maximum lag; start at 1 not zero cause 0 has artifact { flatvalue += abs (audio_buffer[i] - audio_buffer[i + centerlag+1]); perfectvalue += abs (audio_buffer[i] - audio_buffer[i + centerlag]); sharpvalue += abs (audio_buffer[i] - audio_buffer[i + centerlag-1]); if (peakvalueaudio_buffer[i]) troughvalue=audio_buffer[i]; } } if (centerlag0[pbutton0]>int(centerlag0[pbutton0])) { centerlag=(int)centerlag0[pbutton0]; for (int i = 1; i < 200; i++) // was 400=buffer-maximum lag; start at 1 not zero cause 0 has artifact { flatvalue += abs (audio_buffer[i] - audio_buffer[i + centerlag+1]); perfectvalue += abs (audio_buffer[i] - audio_buffer[i + centerlag]); sharpvalue += abs (audio_buffer[i] - audio_buffer[i + centerlag]); if (peakvalueaudio_buffer[i]) troughvalue=audio_buffer[i]; } } //rolling averages !! .9 might be too big averageflatvalue0=averageflatvalue0*.4+flatvalue; averageperfectvalue0=averageperfectvalue0*.4+perfectvalue; averagesharpvalue0=averagesharpvalue0*.4+sharpvalue; //determine greatest correllation lag if ((troughvalue > 0) && (peakvalue < 255) && (peakvalue-troughvalue>25)) // was 25only if not clipping and high signal valu { if (averagesharpvalue0 > averageflatvalue0) pitchcorrection = -1; if (averagesharpvalue0 < averageflatvalue0) pitchcorrection = 1; if ((averagesharpvalue0>averageperfectvalue0)&& (averageflatvalue0>averageperfectvalue0)) pitchcorrection = -3; // was 25define correction deadzone } else { pitchcorrection = 0; } return pitchcorrection; } int autocorrelate1 () //listen to signal to determine final correction //note: autocorrelate is different--uses (0, +1) lags not (+1, -1) for higher res { flatvalue=0; sharpvalue=0; peakvalue=0; troughvalue=255; //perform pseudocorrellation (uses subtraction not multiplication) // lowest values at highest correllation for (int i = 1; i < 400; i++) //250=buffer-maximum lag { flatvalue += abs (audio_buffer[i] - audio_buffer[i + centerlag1[pbutton1]+1]); sharpvalue += abs (audio_buffer[i] - audio_buffer[i + centerlag1[pbutton1]]); if (peakvalueaudio_buffer[i]) troughvalue=audio_buffer[i]; } //rolling averages !! .9 might be too big (this seems to be retained after routine ends) averageflatvalue1=averageflatvalue1*.4+flatvalue; averagesharpvalue1=averagesharpvalue1*.4+sharpvalue; //determine greatest correllation lag if ((troughvalue > 0) && (peakvalue < 255) && (peakvalue-troughvalue>10)) // lower for high string was 25only if not clipping and high signal valu { //digitalWrite(ledCenter, HIGH); if (averagesharpvalue1 > averageflatvalue1) pitchcorrection = -1; if (averagesharpvalue1 < averageflatvalue1) pitchcorrection = 1; } else { pitchcorrection = 0; //digitalWrite(ledCenter, LOW); } return pitchcorrection; } //********Setup void setup () { loadkeymap(); pinMode (r0, INPUT); //hex input 0 digitalWrite (r2, LOW); //turns off pull up resistor pinMode (r1, INPUT); //hex input 1 digitalWrite (r2, LOW); //turns off pull up resistor pinMode (r2, INPUT); //hex input 2 digitalWrite (r2, LOW); //turns off pull up resistor pinMode (r3, INPUT); //hex input 3 digitalWrite (r2, LOW); //turns off pull up resistor pinMode (r4, INPUT); //hex input 4 digitalWrite (r4, LOW); //turns off pull up resistor pinMode (r5, INPUT); //hex input 5 digitalWrite (r4, LOW); //turns off pull up resistor pinMode (plusPin, INPUT); digitalWrite (plusPin, HIGH); //turns on pull up resistor pinMode (minusPin, INPUT); digitalWrite (minusPin, HIGH); //turns on pull up resistor pinMode (refreshPin, INPUT); digitalWrite (refreshPin, HIGH); //turns on pull up resistor pinMode (motor0, OUTPUT); //motor0 output (previously did direct write to ocr2a pin 11??) pinMode (motor1, OUTPUT); //motor1 output //pinMode (pin12, OUTPUT); // *************Some Bit-Banging to get High Speed A/D operation********************* // This is basically assembly language accommodated by the Arduino IDE // The acronyms here are either (1) register names (e.g., ADCSRA is A/D register A) //(see Atmel168 datasheet for register names and use) //or (2) defines/constants (e.g., ADPS2=bit 2) set in the iom168p.h header file buried in the Arduino download //The defines are also described in the Atmel168 datasheet (do a search of the .pdf) // set adc prescaler to 32 for 33kHz sampling frequency (16mhz/32/15 instructions per conversion) sbi (ADCSRA, ADPS2); // This code sets the AD clock prescalar to /32 (16MHz/32=500kHz cbi (ADCSRA, ADPS1); // The A/D clock should be between 50-200 kHz (+ some only 8-bit conversions!) sbi (ADCSRA, ADPS0); // Normal conversion requires about 15 clock cycles so 33Khz conversion rate sbi (ADMUX, ADLAR); // Left justifies A/D output so it can be read in one swoop sbi (ADMUX, REFS0); // Selects VCC Reference for A/D cbi (ADMUX, REFS1); cbi (ADMUX, MUX0); // Set Input Multiplexer to Channel 0 cbi (ADMUX, MUX1); // MUX0-MUX3 select one of 8 A/D inputs Mux0=LSMB cbi (ADMUX, MUX2); // In this case channel 0 is the guitar pickup cbi (ADMUX, MUX3); // TCCR="timer counter control register" // This section sets Timer2 which will control the interrupt timing cbi (TCCR2A, COM2A0); // clear OC2A on Compare Match (output for PWM) pin PB3 ??what is this sbi (TCCR2A, COM2A1); cbi (TCCR2A, COM2B0); // cnew newlear OC2A on Compare Match (output for PWM) pin PB3 ??what is this sbi (TCCR2A, COM2B1); sbi (TCCR2A, WGM20); //Timer2 PWM Mode set to fast PWM (not phase correct mode) sbi (TCCR2A, WGM21); cbi (TCCR2B, WGM22); // Timer2 Clock Prescaler to : 1 (timer driven direclty by I/O clock) sbi (TCCR2B, CS20); //interrupt at 256 countdown 16MHz/256=62kHz cbi (TCCR2B, CS21); // this is too fast, but next choice is /8 (too slow) cbi (TCCR2B, CS22); //so throw out every other ISR (e.g. don't convert in each ISR--see ISR below) //cli(); // disable interrupts to avoid distortion // "timer interrupt mask register" cbi (TIMSK0, TOIE0); // disable Timer0 interrupt !!! Arduino delay functions are turned off--be aware sbi (TIMSK2, TOIE2); // enable Timer2 Interrupt OCR2B=128; //quiet motors at start OCR2A=128; startup=0; //Serial.print("ADC offset="); // Outputs A/D value once so input audio can be DC "trimmed" to a value of 127 //audio_display=audio_in; //Serial.println(audio_display); //start serial with Midi baudrate 31250 or 38400 for debugging //Serial.flush(); //midi !!!! //Serial.begin(31250); */ } //setup //********************************************************************* //********************************************************************* //*******Main loop--motor control and autotuning correctionl *********** //********************************************************************* //********************************************************************* void loop () { //mini os divides all loops into 8 timeslices--this is the timeslice clock if (timeslice<8) timeslice++; else timeslice=0; //********************************************************************** // every timeslice //********************************************************************** //heartbeart /*if (toggle==1) digitalWrite(led1, HIGH); else digitalWrite(led1,LOW); if (toggle==1) toggle=0; else toggle=1; */ //all timers outside of slices for simplicity if (debounce<5000) debounce++; if (startup<500000) startup++; if ((holdoff0<100000)&& (motorhasstopped0=HIGH)) holdoff0++; //start holdoff0 timer for gain if motor stopped if ((holdoff1<100000)&& (motorhasstopped1=HIGH)) holdoff1++; // save keymap by pressing button if (digitalRead(refreshPin)==LOW) { savekeymap(); int pause=0; digitalWrite(led0, HIGH); delay(100000); //not normal value becuase I messed with the timers digitalWrite(led0, LOW); } // check for tuning up or down if (((digitalRead(plusPin)==LOW)) && (debounce>500)) { debounce=0; if (tuningchannel==0) { //delay(10000); keymap0[pbutton0]=keymap0[pbutton0]+1; if (keymap0[pbutton0]>254) keymap0[pbutton0]=254; command0 = keymap0[pbutton0]; slidecommand0=command0; } if (tuningchannel==1) { //delay(10000); keymap1[pbutton1]=keymap1[pbutton1]+1; if (keymap1[pbutton1]>254) keymap1[pbutton1]=254; command1 = keymap1[pbutton1]; slidecommand1=command1; } } if ((digitalRead(minusPin)==LOW)&& (debounce>500)) //press tne down button { { debounce=0; if (tuningchannel==0) { keymap0[pbutton0]=keymap0[pbutton0]-1; if (keymap0[pbutton0]<1) keymap0[pbutton0]=1; command0 = keymap0[pbutton0]; } if (tuningchannel==1) { keymap1[pbutton1]=keymap1[pbutton1]-1; if (keymap1[pbutton1]<1) keymap1[pbutton1]=1; command1 = keymap1[pbutton1]; } } } if ((digitalRead(plusPin)==HIGH) && (digitalRead(minusPin)==HIGH)) debounce=0; //********************************************************************** // high priority control loop time slices 0, 2, 4, 6 (try to keep this slice at >5khz) //********************************************************************** // *********************** // FIRST MOTOR (MOTOR 0) // *********************** if ((timeslice==0) || (timeslice==2) || (timeslice==4) || (timeslice==6)) { // ramp up gain after a time delay if (holdoff0 >= 2000)errorsignal0=((collectedcommand0 - sensor_in0)*4); else if (holdoff0 >= 1000)errorsignal0=((collectedcommand0 - sensor_in0)*3); else if (holdoff0 >= 900) errorsignal0 = ((collectedcommand0 - sensor_in0)*2); else if (holdoff0 >= 80) errorsignal0 = ((collectedcommand0 - sensor_in0)); else errorsignal0 = ((collectedcommand0 - sensor_in0)/2); errorsignal0=errorsignal0+128; // trap error signal overflow if (errorsignal0 > 255) errorsignal0=255; if (errorsignal0 < 1) errorsignal0=10; //output error to motor if (startup<10000) OCR2B=128; else OCR2B=errorsignal0; // *********************** // SECOND MOTOR () // *********************** // ramp up gain after a time delay if (holdoff1 >= 2000)errorsignal1=((collectedcommand1 - sensor_in1)*4); //was 100 else if (holdoff1 >= 1000)errorsignal1=((collectedcommand1 - sensor_in1)*3); //was 100 else if (holdoff1 >= 900) errorsignal1 = ((collectedcommand1 - sensor_in1)*2); //was 90 else if (holdoff1 >= 80) errorsignal1 = ((collectedcommand1 - sensor_in1)); else errorsignal1 = ((collectedcommand1 - sensor_in1)/2); errorsignal1=errorsignal1+128; // trap error signal overflow if (errorsignal1 > 255) errorsignal1=255; if (errorsignal1 < 1) errorsignal1=10; //output error to motor if (startup<10000) OCR2A=128; else OCR2A=errorsignal1; } //end of time slices 0, 2, 4, 6 //********************************************************************** // detect motor stop time slice 1 //********************************************************************** if (timeslice==1) { analogport0read(); //get current midi notes for channel 0 // *********************** //pb okay to here // FIRST MOTOR (MOTOR 0) // *********************** // DETECT MOTOR HAS STOPPED AFTER RUNNING***************** if (stoptimeout0<7000) stoptimeout0++; steadyerror0=((command0-int(sensor_in0))/2+128); //check close to commanded position if (((steadyerror0>120)&&(steadyerror0<136))|| (stoptimeout0>5000)) stopcounter0++; //if so start counting else stopcounter0=0; //consider adding a timeout for stop even if dont get close to mark at 1 second. if (stopcounter0>2) //after a while assume motor has stopped { motorhasstopped0=HIGH; } else { //motorhasstopped0=LOW; //!!!completely remove motor stop detection!!!! } if (motorhasstopped0==LOW) holdoff0=0; if (stopcounter0>900) stopcounter0=900; // *********************** // SECOND MOTOR (motor 1) // *********************** // DETECT MOTOR HAS STOPPED AFTER RUNNING***************** if (stoptimeout1<7000) stoptimeout1++; steadyerror1=((command1-int(sensor_in1))/2+128); //check close to commanded position if (((steadyerror1>120)&&(steadyerror1<136)) || (stoptimeout1>5000)) stopcounter1++; //if so start counting else stopcounter1=0; if (stopcounter1>2) // after a while assume motor has stopped { motorhasstopped1=HIGH; } else { //motorhasstopped1=LOW; } if (motorhasstopped1==LOW) holdoff1=0; if (stopcounter1>900) stopcounter1=900; } //end of timeslice1 //********************************************************************** // detect new note, calculate slides, time slice 3 //********************************************************************** if (timeslice==3) { analogport1read(); //get current midi notes for channel 1 // *********************** // FIRST MOTOR (MOTOR 0) // *********************** // NEW KEYPRESS DETECTION*************************** //new note triggers many actions if (pbutton0!=lastbutton0) { // reset test for new buttonpress previousbutton0 = lastbutton0; lastbutton0 = pbutton0; // store tune correction and countervalues accumulated so far if (tunecorrection0>1) tunecorrection0=1; //because of friction in static correction limit change note to 1 if (tunecorrection0<-1) tunecorrection0=-1; keymap0[previousbutton0]=keymap0[previousbutton0]+tunecorrection0; //save autotuning effect tunecorrection0=0; //reset lots of stuff correctioncounter0=0; do_once0=HIGH; motorhasstopped0=LOW; stopcounter0=0; holdoff0=0; averagesharpvalue0=0; averageflatvalue0=0; averageperfectvalue0=0; //slide variables lastcommand0=slidecommand0; slidetimer0=0; command0 = keymap0[pbutton0]; //get tension command based on pressed button slideincrement0=((command0-lastcommand0)/10);// was 10 tuningchannel=0; //which string am I tuning if I received a tuning command } //end of new note actions // SLIDE CALCULATIONS************************** // slide stuff if (slidetimer0<1000) slidetimer0++; if ((slideincrement0<10) && (slideincrement0>0)) slideincrement0=100; //just jump to note for close notes if ((slideincrement0<0) && (slideincrement0>-10)) slideincrement0=-100; if (slidetimer0>10) //5, 10, 15 is good { slidecommand0+=slideincrement0; slidetimer0=0; } if ((slideincrement0<=0) && (slidecommand0=0) && (slidecommand0>command0)) slidecommand0=command0; //get rid of remainder errors collectedcommand0=(slidecommand0);// + tunecorrection0); // + hysteresismap[indx]); //collectedcommand0=command0; //deactivates slide if (collectedcommand0>255) collectedcommand0=255; if (collectedcommand0<0) collectedcommand0=1; // *********************** // SECOND MOTOR 1 // *********************** // NEW KEYPRESS DETECTION*************************** //new note triggers many actions if (pbutton1!=lastbutton1) { // reset test for new buttonpress previousbutton1 = lastbutton1; lastbutton1 = pbutton1; // store tune correction and countervalues accumulated so far if (tunecorrection1>1) tunecorrection1=1; //because of friction in static correction limit change note to 1 if (tunecorrection1<-1) tunecorrection1=-1; keymap1[previousbutton1]=keymap1[previousbutton1]+tunecorrection1; //save autotuning effect tunecorrection1=0; //reset lots of stuff correctioncounter1=0; do_once1=HIGH; motorhasstopped1=LOW; stopcounter1=0; holdoff1=0; averagesharpvalue1=0; averageflatvalue1=0; averageperfectvalue1=0; stoptimeout1=0; //slide variables lastcommand1=slidecommand1; slidetimer1=0; command1 = keymap1[pbutton1]; //get tension command based on pressed button slideincrement1=((command1-lastcommand1)/10); //was 10 tuningchannel=1; //which string am I tuning if I received a tuning command } //end of new note actions // SLIDE CALCULATIONS************************** // slide stuff if (slidetimer1<1000) slidetimer1++; if ((slideincrement1<10) && (slideincrement1>0)) slideincrement1=100; //just jump to note for close notes if ((slideincrement1<0) && (slideincrement1>-10)) slideincrement1=-100; if (slidetimer1>10) //5 okay 20 too big { slidecommand1+=slideincrement1; slidetimer1=0; } if ((slideincrement1<=0) && (slidecommand1=0) && (slidecommand1>command1)) slidecommand1=command1; //get rid of remainder errors collectedcommand1=(slidecommand1);// + tunecorrection1); // + hysteresismap[indx]); //collectedcommand1=command1; //turns off slide if (collectedcommand1>255) collectedcommand1=255; if (collectedcommand1<0) collectedcommand1=1; }//end of time slice 3 //********************************************************************** // autocorrelate 0, time slice 5 //********************************************************************** if (timeslice==5) { if (skipafew<30) skipafew++; else skipafew=0; //pb okay here // *********************** // FIRST MOTOR (MOTOR 0) // *********************** // *********************************************************************** // ***********DO MICROCORRECTIONS ONCE MOTOR HAS STOPPED******************** // ************************************************************************* if ((holdoff0>80) && (skipafew==0)) //gain has settled out so can check frequency (loop update can slow because low motion) { //digitalWrite(led1,HIGH); // *******SET UP FOR AUTOCORRELLATION //switch A/D to audio input to get 512 samples of guitar for autocorrelation servosense=false; buffer_index=0; //set to zero but use starting at 1 cause first data is bad (pre-conversion start) cbi (ADMUX, MUX0); // Set Input Multiplexer to Channel 2 for string 0 pickup input sbi (ADMUX, MUX1); // MUX0-3 select one of 8 A/D inputs Mux0=LSMB cbi (ADMUX, MUX2); cbi (ADMUX, MUX3); sampling=true; //enable sampling in ISR while (sampling==true); //spin until acquistion is complete //set back to normal sensor sampling for motor control half3=true; //reset to tension sampling sbi (ADMUX, MUX0); // Set Input Multiplexer to Channel 0 for tension sensor 0 cbi (ADMUX, MUX1); // MUX0-3 select one of 8 A/D inputs Mux0=LSMB cbi (ADMUX, MUX2); cbi (ADMUX, MUX3); servosense=true; sharp_flat0 = autocorrelate0 (); // ************INTEGRATE/AVERAGE AUTOCORRELATION VALUES if ((sharp_flat0!=0)&& (holdoff0>2000))// update tuning only if good pickup data and stable { if (sharp_flat0 == 1) //new trigger for starting correction based on gain { // perfectcounter0++; correctioncounter0 = correctioncounter0 - 1; digitalWrite(led0, HIGH); } /* if (sharp_flat0 == -3) //perfect { digitalWrite (led0, LOW); digitalWrite (led1, LOW); // perfectcounter0++; } */ if (sharp_flat0 == -1) //flat { //perfectcounter0--; correctioncounter0 = correctioncounter0 + 1; digitalWrite(led1, HIGH); } tunecorrection0 = (int) correctioncounter0 / 10; //adjust this for tuning correction speed was 10 } //end of update tuning only if good data else { digitalWrite(led0, LOW); digitalWrite(led1, LOW); } } //end of microcorrections }//end of timeslice 5 //********************************************************************** // autocorrelate 1, time slice 7 //********************************************************************** if (timeslice==7) { // *********************** // SECOND MOTOR (MOTOR 1) // *********************** // *********************************************************************** // ***********DO MICROCORRECTIONS ONCE MOTOR HAS STOPPED******************** // ************************************************************************* if ((holdoff1>80) && (skipafew==15)) //gain has settled out so can check frequency (loop update can slow because low motion) { //digitalWrite(led1,LOW); huh?? // *******SET UP FOR AUTOCORRELLATION //switch A/D to audio input to get 512 samples of guitar for autocorrelation servosense=false; buffer_index=0; sbi (ADMUX, MUX0); // Set Input Multiplexer to Channel 3 for string 1 pickup input sbi (ADMUX, MUX1); // MUX0-3 select one of 8 A/D inputs Mux0=LSMB cbi (ADMUX, MUX2); cbi (ADMUX, MUX3); sampling=true; //enable sampling in ISR while (sampling==true); //spin until acquistion is complete //set back to normal sensor sampling for motor control half3=true; //reset to tension sampling sbi (ADMUX, MUX0); // Set Input Multiplexer to Channel 0 for tension sensor 0 cbi (ADMUX, MUX1); // MUX0-3 select one of 8 A/D inputs Mux0=LSMB cbi (ADMUX, MUX2); cbi (ADMUX, MUX3); servosense=true; sharp_flat1 = autocorrelate1 (); // ************INTEGRATE/AVERAGE AUTOCORRELATION VALUES if ((sharp_flat1!=0)&& (holdoff1>2000))// update tuning only if good pickup data { if (sharp_flat1 == 1) //new delay for correctioncoutner based on gain switching //sharp { correctioncounter1 = correctioncounter1 - 1; digitalWrite(led0, HIGH); } if (sharp_flat1 == -1) //flat { correctioncounter1 = correctioncounter1 + 1; digitalWrite(led1, HIGH); } tunecorrection1 = (int) correctioncounter1 / 10; // was 20adjust this for tuning correction speed was 10 } //end of update tuning only if good data else { digitalWrite(led0, LOW); digitalWrite(led1, LOW); } } //end of microcorrections }// end of timeslice 7 } //main loop // **************************************************************** // ****************END OF MAIN LOOP****************************** // **************************************************************** // **************************************************************** // ****************INTERRUPT SERVICE ROUTINE*********************** // **************************************************************** /* ***************************************************************** * Timer2 Interrupt Service at 31.25 kHz KHz (too fast for ADC so divide by 2 here) *!!!ACTUALLY CLOCKING AT 62KhZ (per oscilliscope) and A/D CAN HANDLE 32KHZ AT 8 BITS-- * SO MAY SAMPLE AUDIO AT 15 AND ALTERNATE SENSORS AT 32--need to check math * here the audio is sampled in a rate of: 16Mhz / 256 / 4 = 15625 Hz ******************************************************************* */ ISR (TIMER2_OVF_vect) //how avr-gcc describes an interrupt { half1 = !half1; if (half1) //cut interrupt rate in half to 32kHz { half2=!half2; if (half2) //cut interrupt rate in half to 15625 Hz (Max A/D speed) { //on demand sensing if (sampling==true) //get non servo data--from pickups or from analogport { if (midisense) //get latest midi value from mididecoder analog channel { midiin=ADCH; //MUX value is set by analogportread buffer_index++; if (buffer_index > 1) //need to toss first data because sammpling only at end of isr sampling=false; } else //get 512 samples of audio (determinded from autocorrelate argument) { //PORTB = PORTB | 16 ; //set pin 12 to profile ISR //MUX value is set by autocorrelate audio_buffer[buffer_index] = ADCH; // store sample in audio buffer buffer_index++; if (buffer_index > 511) sampling=false; //PORTB = PORTB ^ 16 ; //set pin 12 to profile ISR } } } if (servosense) { //background sensing half3 = !half3; if (half3) //get pin 0 (mode) { // measure motor1 sensor here sensor_in1 = ADCH; cbi (ADMUX, MUX0); // Set Input Multiplexer to analog input o and sensor 0 cbi (ADMUX, MUX1); //consider removing these instructions for speed cbi (ADMUX, MUX2); cbi (ADMUX, MUX3); } else //get pin 5 (sensor) { // measure motor0 sensor here sensor_in0 = ADCH;//0000 sbi (ADMUX, MUX0); // Set Input Multiplexer to analog input 1 cbi (ADMUX, MUX1); cbi (ADMUX, MUX2); cbi (ADMUX, MUX3); } } dummy++; dummy--; dummy++; dummy--; // short delay before start conversion !!hmm why? sbi (ADCSRA, ADSC); // start next conversion } }