Frequency to MIDI code

Hi folks,

I'm struggling with getting this code to be a bit more efficient. Basically right now it detects the frequency from a guitar and then converts the frequency detected into its relative MIDI note, I've got it set to only detect and play MIDI notes from 40-69. It works and it can play the correct note that is played from the guitar but the main problem I have now is that it also plays other notes around it, I'm not too sure of the cause of it now but my guess is that it's from the initial hit from the string or feedback produced. Only solutions I can think of are to reduce the rate it can pick up notes and maybe adding a short delay before it sends out the frequency to MIDI to try and get rid of any inconsistencies when hitting the string.

Any help would be appreciated greatly thanks.

Here's the code I have so far:

#define DEBUG_MODE 0  // 1 = text output to Serial Monitor, 0 = binary to Hairless MIDI

//clipping indicator variables
boolean clipping = 0;

//data storage variables
byte newData = 0;
byte prevData = 0;
unsigned int time = 0;    //keeps time and sends vales to store in timer[] occasionally
int timer[10];            //sstorage for timing of events
int slope[10];            //storage fro slope of events
unsigned int totalTimer;  //used to calculate period
unsigned int period;      //storage for period of wave
byte index = 0;           //current storage index
float frequency;          //storage for frequency calculations
int maxSlope = 0;         //used to calculate max slope as trigger point
int newSlope;             //storage for incoming slope data

//variables for decided whether you have a match
byte noMatch = 0;   //counts how many non-matches you've received to reset variables if it's been too long
byte slopeTol = 3;  //slope tolerance- adjust this if you need
int timerTol = 10;  //timer tolerance- adjust this if you need

//for MIDI instructions
constexpr int noteON = 144;   //144 = 10010000 in binary, note on command
constexpr int noteOFF = 128;  //128 = 10000000 in binary, note off command

int velocity = 100;

//Full spectrum of notes


const float MIDI_FREQUENCY[]{
  //	C			C-sharp		D			D-sharp		E			F			F-sharp		G			G-sharp		A			A-sharp		B
  8.18, 8.66, 9.18, 9.73, 10.30, 10.92, 11.56, 12.25, 12.98, 13.75, 14.57, 15.44,                              // Octave -1
  16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87,                          // Octave 0
  32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91, 55.00, 58.27, 61.74,                          // Octave 1
  65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.83, 110.00, 116.54, 123.47,                      // Octave 2
  130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 233.08, 246.94,              // Octave 3
  261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88,              // Octave 4
  523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77,              // Octave 5
  1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760.00, 1864.66, 1975.53,  // Octave 6
  2093.00, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520.00, 3729.31, 3951.07,  // Octave 7
  4186.01, 4434.92, 4698.63, 4978.03, 5274.04, 5587.65, 5919.91, 6271.93, 6644.88, 7040.00, 7458.62, 7902.13,  // Octave 8
  8372.02, 8869.84, 9397.26, 9956.06, 10548.08, 11175.30, 11839.82, 12543.86                                   // Octave 9 (partial)
};


//Test Spectrum of Notes

/*

const float MIDI_FREQUENCY[]{
  //	E			F			F-sharp		G			G-sharp		A			A-sharp		B     C			C-sharp		D			D-sharp
  8.18, 8.66, 9.18, 9.73, 10.30, 10.92, 11.56, 12.25, 12.98, 13.75, 14.57, 15.44,                              // Octave -1
  16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87,                          // Octave 0
  32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91, 55.00, 58.27, 61.74,                          // Octave 1
  65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.83, 110.00, 116.54, 123.47,                      // Octave 2
  130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 233.08, 246.94,              // Octave 3
  261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88,              // Octave 4
  523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77,              // Octave 5
  1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760.00, 1864.66, 1975.53,  // Octave 6
  2093.00, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520.00, 3729.31, 3951.07,  // Octave 7
  4186.01, 4434.92, 4698.63, 4978.03, 5274.04, 5587.65, 5919.91, 6271.93, 6644.88, 7040.00, 7458.62, 7902.13,  // Octave 8
  8372.02, 8869.84, 9397.26, 9956.06, 10548.08, 11175.30, 11839.82, 12543.86                                   // Octave 9 (partial)
};

*/

const uint8_t MIDI_NOTE_C0 = 40;
const uint8_t MIDI_NOTE_C9 = 69;
static byte LastNote = 0;

void setup()
{
  Serial.begin(9600);

  pinMode(13, OUTPUT);  //led indicator pin
  pinMode(12, OUTPUT);  //output pin

  cli();  //diable interrupts

  //set up continuous sampling of analog pin 0 at 38.5kHz

  //clear ADCSRA and ADCSRB registers
  ADCSRA = 0;
  ADCSRB = 0;

  ADMUX |= (1 << REFS0);  //set reference voltage
  ADMUX |= (1 << ADLAR);  //left align the ADC value- so we can read highest 8 bits from ADCH register only

  ADCSRA |= (1 << ADPS2) | (1 << ADPS0);  //set ADC clock with 32 prescaler- 16mHz/32=500kHz
  ADCSRA |= (1 << ADATE);                 //enabble auto trigger
  ADCSRA |= (1 << ADIE);                  //enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN);                  //enable ADC
  ADCSRA |= (1 << ADSC);                  //start ADC measurements

  sei();  //enable interrupts
}

ISR(ADC_vect)
{  //when new ADC value ready

  PORTB &= B11101111;  //set pin 12 low
  prevData = newData;  //store previous value
  newData = ADCH;      //get value from A0
  if (prevData < 127 && newData >= 127)
  {                                 //if increasing and crossing midpoint
    newSlope = newData - prevData;  //calculate slope
    if (abs(newSlope - maxSlope) < slopeTol)
    {  //if slopes are ==
      //record new data and reset time
      slope[index] = newSlope;
      timer[index] = time;
      time = 0;
      if (index == 0)
      {                      //new max slope just reset
        PORTB |= B00010000;  //set pin 12 high
        noMatch = 0;
        index++;  //increment index
      }
      else if (abs(timer[0] - timer[index]) < timerTol && abs(slope[0] - newSlope) < slopeTol)
      {  //if timer duration and slopes match
        //sum timer values
        totalTimer = 0;
        for (byte i = 0; i < index; i++)
        {
          totalTimer += timer[i];
        }
        period = totalTimer;  //set period
        //reset new zero index values to compare with
        timer[0] = timer[index];
        slope[0] = slope[index];
        index = 1;           //set index to 1
        PORTB |= B00010000;  //set pin 12 high
        noMatch = 0;
      }
      else
      {           //crossing midpoint but not match
        index++;  //increment index
        if (index > 9)
        {
          reset();
        }
      }
    }
    else if (newSlope > maxSlope)
    {  //if new slope is much larger than max slope
      maxSlope = newSlope;
      time = 0;  //reset clock
      noMatch = 0;
      index = 0;  //reset index
    }
    else
    {             //slope not steep enough
      noMatch++;  //increment no match counter
      if (noMatch > 9)
      {
        reset();
      }
    }
  }

  if (newData == 0x00 || newData == 0xFF)
  {                      //if clipping
    PORTB |= B00100000;  //set pin 13 high- turn on clipping indicator led
    clipping = 1;        //currently clipping
  }

  time++;  //increment timer at rate of 38.5kHz
}

void reset()
{                //clea out some variables
  index = 0;     //reset index
  noMatch = 0;   //reset match couner
  maxSlope = 0;  //reset slope
}

void checkClipping()
{  //manage clipping indicator LED
  if (clipping)
  {                      //if currently clipping
    PORTB &= B11011111;  //turn off clipping indicator led
    clipping = 0;
  }
}

void loop()
{
  checkClipping();

  frequency = 38462 / float(period);  //calculate frequency timer rate/period


/*
#if DEBUG_MODE
  //print results
  Serial.print(frequency);
  Serial.println(" hz");
#endif
*/

  //MIDI notes

  for (byte note = MIDI_NOTE_C0; note < MIDI_NOTE_C9; note++)
  {
    if (frequency < (MIDI_FREQUENCY[note] + MIDI_FREQUENCY[note + 1]) / 2)
    {
      // Matching note!
      if (note != LastNote)
      {
        // The note has changed so turn off the previous note
        MIDImessage(noteOFF, LastNote, velocity);
        MIDImessage(noteON, note, velocity);
      }

      LastNote = note;
      break;  // No need to look for more matches.
    }
  }
}

//send MIDI message
void MIDImessage(byte command, byte MIDInote, byte MIDIvelocity)
{
  Serial.write(command);       //send note on or note off command 
  Serial.write(MIDInote);      //send pitch data
  Serial.write(MIDIvelocity);  //send velocity data

/*
  Serial.print(command);       //send note on or note off command
  Serial.print(MIDInote);      //send pitch data
  Serial.print(MIDIvelocity);  //send velocity data
  Serial.println(" ");
  */
}


/*
//send MIDI message
void MIDImessage(byte command, byte MIDInote, byte MIDIvelocity)
{
#if DEBUG_MODE
  // Display each MIDI message as text for debugging
  switch (command)
  {
    case noteON: Serial.print(noteON); break;
    case noteOFF: Serial.print(noteOFF); break;
    default:
      Serial.print(command);  // show note on or note off command
      break;
  }
  
  //Serial.print(", ");
  Serial.print(MIDInote);  // show pitch data
  //Serial.print(", ");
  Serial.print(MIDIvelocity);  // send velocity data
  
#else
  // Send each MIDI message to Hairless MIDI as three binary bytes
  Serial.write(command);       //send note on or note off command
  Serial.write(MIDInote);      //send pitch data
  Serial.write(MIDIvelocity);  //send velocity data

  Serial.print(command);       //send note on or note off command
  Serial.print(MIDInote);      //send pitch data
  Serial.print(MIDIvelocity);  //send velocity data
  Serial.println(" ");
#endif
}
*/ 

/*

//send MIDI message
void MIDImessage(byte command, byte MIDInote, byte MIDIvelocity)
{
#if DEBUG_MODE
  // Display each MIDI message as text for debugging
  switch (command)
  {
    case noteON: Serial.print("noteON"); break;
    case noteOFF: Serial.print("noteOFF"); break;
    default:
      Serial.print(command);  // show note on or note off command
      break;
  }
  Serial.print(", ");
  Serial.print(MIDInote);  // show pitch data
  Serial.print(", ");
  Serial.println(MIDIvelocity);  // send velocity data
#else
  // Send each MIDI message to Hairless MIDI as three binary bytes
  Serial.write(command);       //send note on or note off command
  Serial.write(MIDInote);      //send pitch data
  Serial.write(MIDIvelocity);  //send velocity data

  Serial.print(command);       //send note on or note off command
  Serial.print(MIDInote);      //send pitch data
  Serial.print(MIDIvelocity);  //send velocity data
#endif
}
*/

You are probably having problems with harmonics, probably the third harmonic. The best way to eliminate them is to use DSP (Digital Signal Processing). I do not know of any musical instrument that does not generate harmonics but I might learn about one here.

yeah that makes sense actually i hadnt thought of that before but it makes sense, is there like a bit of software/app that i can run to help filter it out or is it something ill need to add to the code?

I would assume there is but I am not familiar with it. What I was familiar with was a few years back and the processor is no longer being sold for new applications and it was a masked part, expensive to get the first few.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.