Arduino and MIDI

Discussion in 'Embedded Systems and Microcontrollers' started by DerStrom8, Jul 23, 2014.

  1. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    Hi guys,

    Just wondering, are there any Arduino and MIDI experts here? I keep hitting a brick wall in my program to convert MIDI to a polyphonic square wave. I'm reading the MIDI data just fine, and I can play monophonic sounds, but polyphonic audio is beginning to bug the....out of me! Just looking for someone I can bounce some ideas around with, who might be able to point me in the right direction.

    Thanks guys!
  2. ghebaur


    Jun 17, 2014
    It's not easy to get polyphony only with arduino. Even if you had more PWM outputs on arduino it wouldn't be any good becouse arduino is slow and it will crush.

    I'm planning to make a synth myself and i'm considering to use a synth wavetable chip(i forgot how it's actually called), most of them have General midi sounds. This is the easiest way i know about.

    You can also check this page. This project particularly looks very interesting.
  3. mitko89


    Sep 20, 2012
  4. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    I've done MIDI in to polyphonic audio out (8 notes). It was a bit tight on a PIC16F. Should be easy enough on a 18F chip at 40MHz (or the right arduino).

    To generate the polyphonic notes I used a timer interrupt faster than the highest note frequency, and bresenham type note generation (pin toggling square waves).

    What is your output? Multiple pins, or mixed into one PWM?
  5. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    Ideally I would like output from a single pin (so mixed into one PWM), but I have 6 extra I/O ports in case I need them.
  6. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    Is squarewave ok? Or did you want to mix multiple sines? That will require more processing power.

    Do you know the highest freq you need to generate?

    And the max number of polyphonic notes?
  7. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    I definitely need it to be a square wave. A sine wave will not work at all. I would say 2kHz is probably the maximum, and even that is allowing some room. Max number of polyphonic notes should probably be as many as possible, but 6 should be fine.
  8. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    Hi The RB,

    If it helps, I am using this code to process the MIDI signal. However, since this program uses the native tone library, it can only play one tone at a time. My goal is to modify it so that it can play more than one note at a time:

    Code ( (Unknown Language)):
    1. // A very simple MIDI synth.
    2. // Greg Kennedy 2011
    4. #include <avr/pgmspace.h>
    6. #define statusLed 13
    7. #define tonePin 7
    9. // MIDI channel to answer to, 0x00 - 0x0F
    10. #define myChannel 0x00
    11. // set to TRUE and the device will respond to all channels
    12. #define respondAllChannels false
    14. // midi commands
    15. #define MIDI_CMD_NOTE_OFF 0x80
    16. #define MIDI_CMD_NOTE_ON 0x90
    17. #define MIDI_CMD_KEY_PRESSURE 0xA0
    19. #define MIDI_CMD_PROGRAM_CHANGE 0xC0
    20. #define MIDI_CMD_CHANNEL_PRESSURE 0xD0
    21. #define MIDI_CMD_PITCH_BEND 0xE0
    23. // this is a placeholder: there are
    24. //  in fact real midi commands from F0-FF which
    25. //  are not channel specific.
    26. // this simple synth will just ignore those though.
    27. #define MIDI_CMD_SYSEX 0xF0
    29. // a dummy "ignore" state for commands which
    30. //  we wish to ignore.
    31. #define MIDI_IGNORE 0x00
    33. // midi "state" - which data byte we are receiving
    34. #define MIDI_STATE_BYTE1 0x00
    35. #define MIDI_STATE_BYTE2 0x01
    37. // MIDI note to frequency
    38. //  This isn't exact and may sound a bit detuned at lower notes, because
    39. //  the floating point values have been rounded to uint16.
    40. //  Based on A440 tuning.
    42. // I would prefer to use the typedef for this (prog_uint16_t), but alas that triggers a gcc bug
    43. // and does not put anything into the flash memory.
    45. // Also note the limitations of tone() which at 16mhz specifies a minimum frequency of 31hz - in other words, notes below
    46. // B0 will play at the wrong frequency since the timer can't run that slowly!
    47. uint16_t frequency[128] PROGMEM = {8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 28, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699, 4978, 5274, 5588, 5920, 5920, 6645, 7040, 7459, 7902, 8372, 8870, 9397, 9956, 10548, 11175, 11840, 12544};
    49. //setup: declaring iputs and outputs and begin serial
    50. void setup() {
    51.   pinMode(statusLed,OUTPUT);   // declare the LED's pin as output
    53.   pinMode(tonePin,OUTPUT);           // setup tone output pin
    55.   //start serial with midi baudrate 31250
    56.   // or 38400 for debugging (eg MIDI over serial from PC)
    57.   Serial.begin(31250);
    59.   // indicate we are ready to receive data!
    60.   digitalWrite(statusLed,HIGH);
    61. }
    63. //loop: wait for serial data
    64. void loop () {
    65.   static byte note;
    66.   static byte lastCommand = MIDI_IGNORE;
    67.   static byte state;
    68.   static byte lastByte;
    70.   while (Serial.available()) {
    72.     // read the incoming byte:
    73.     byte incomingByte =;
    75.     // Command byte?
    76.     if (incomingByte & 0b10000000) {
    77.       if (respondAllChannels ||
    78.              (incomingByte & 0x0F) == myChannel) { // See if this is our channel
    79.         lastCommand = incomingByte & 0xF0;
    80.       } else { // Not our channel.  Ignore command.
    81.         lastCommand = MIDI_IGNORE;
    82.       }
    83.       state = MIDI_STATE_BYTE1; // Reset our state to byte1.
    84.     } else if (state == MIDI_STATE_BYTE1) { // process first data byte
    85.       if ( lastCommand==MIDI_CMD_NOTE_OFF )
    86.       { // if we received a "note off", make sure that is what is currently playing
    87.         if (note == incomingByte) noTone(tonePin);
    88.         state = MIDI_STATE_BYTE2; // expect to receive a velocity byte
    89.       } else if ( lastCommand == MIDI_CMD_NOTE_ON ){ // if we received a "note on", we wait for the note (databyte)
    90.         lastByte=incomingByte;    // save the current note
    91.         state = MIDI_STATE_BYTE2; // expect to receive a velocity byte
    92.       }
    93.       // implement whatever further commands you want here
    94.     } else { // process second data byte
    95.       if (lastCommand == MIDI_CMD_NOTE_ON) {
    96.         if (incomingByte != 0) {
    97.           note = lastByte;
    98.           tone(tonePin,(unsigned int)pgm_read_word(&frequency[note]));
    99.         } else if (note == lastByte) {
    100.           noTone(tonePin);
    101.         }
    102.       }
    103.       state = MIDI_STATE_BYTE1; // message data complete
    104.                                  // This should be changed for SysEx
    105.     }
    106.   }
    107. }
    I have been looking into the following non-native Tone library that allows polyphonic sounds, though it requires that you only have one tone per pin:

    The trouble is turning off the note once it's turned on. Indexing the playing notes and turning of a specific one is confusing me.

    I guess this is where I need help, or other suggestions. Mixing all square waves into a single one is really important as well.

  9. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    Ignoring the MIDI side for now, you need a way to generate multiple independent frequencies in a timer interrupt.

    The method I used was a timer interrupt about 10 times faster than the highest note, then used a bresenham timing system which can generate any not freq from any interrupt frequency. The benefit of that versatility is that the same interrupt frequency (one freq) can generate all different note freqs.

    Assuming a 10MHz 8bit timer, set to prescaler 2:1 will make a timer interrupt every 10mil/512 or 19531 Hz. That's about 10 times your highest note of 2000Hz, which is fine.

    Then the code looks like this;
    Code ( (Unknown Language)):
    2. unsigned int bres1, bres2;  // 16bit varsd for the bresenham accumulators
    4. // (gets here in interrupt at 19531 Hz)
    6. // this generates an A note at 440Hz
    7. bres1 += 880;  // 440*2 needed to toggle pin at 440Hz
    8. if(bres1 >= 19531)
    9. {
    10.   bres1 -= 19531;
    11.   PORTB.F1 = ~PORTB.F1;  // toggle pin1 at 440Hz
    12. }
    14. // this simultaneously generates a C note at 523Hz
    15. bres2 += 1046;  // 523*2 needed
    16. if(bres2 >= 19531)
    17. {
    18.   bres2 -= 19531;
    19.   PORTB.F2 = ~PORTB.F2;  // toggle pin2 at 523Hz
    20. }
    That goes inside the interrupt, and makes two notes at the same time, each note is a squarewave on its own digital output pin.

    That should give you the basic idea. You can improve the code a lot by doubling the 19531 constant to 39062, which lets you use twice the resolution in the note freq (so 440 becomes 440*4).

    The bresenham math is very fast and simple and maintains the freq ratio between the two frequencies (int freq at 19531Hz and note freq at 440Hz).

    Obviously you need some trimmings like a flag to turn each note on/off, and to use a variable for 440*2 not a constant, so the note can be set elsewhere;
    Code ( (Unknown Language)):
    2. // this generates one note
    3. if(note1on==1)  // now this flag turns sound on/off
    4. {
    5.   bres1 += note1freq;  // now uses a var for the note freq
    6.   if(bres1 >= 19531)
    7.   {
    8.     bres1 -= 19531;
    9.     PORTB.F1 = ~PORTB.F1;  
    10.   }
    11. }
    DerStrom8 likes this.
  10. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    Thanks very much! This will definitely help, though I'll need to read it again tomorrow after my mind has rested :p

    I guess the other question is what would be the preferred way to mix the signals? I will need to end up with a single PWM signal, so if I have a tone output from several pins, I'll need some sort of mixer.

    Thanks again for the suggestions.
  11. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    Okay, so after some further work, it looks like my criteria may need to change a bit. It looks like I'll need to keep my on-time below 1mS, which means I won't be able to use the Tone library on Arduino after all. I will have to use the timers like you recommended already.

    I will be using the MIDI library for Arduino, which retrieves all three bytes at once and gives you direct access to the command, the channel, the note, and the velocity. I have a function that determines the frequency from the note, which means I can obtain the period, and thus the required duty cycle. The part I am trying to figure out is how to play a polyphonic sound without holding up the rest of the program. I'm not familiar with the bresenham timing system, so I've been reading through your posts over and over again to try to understand it.

    Timer0 and Timer2 are 8-bit (Timer0 is used for delay() functions) and Timer1 is 16-bit. The clock speed is 16MHz.

    Once again, it would be best if I can have a PWM signal on a single output pin that is a mix of all the tones. At the end I need a single output anyway, and I would like to avoid extra circuitry to mix the signals if possible.
  12. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    Just an update:

    I believe I have the indexing down--I'm storing a note in an array, and now I just need a function that runs "constantly" (not really, but close to) that looks at the array and plays the notes in the array. Question is should I use the timer/interrupt to run said function, and also, how would you suggest I play the specified note at a duty cycle that prevents the output from being on for more than 1mS at a time? Obviously, if the note frequency is lower than 500 Hz then I can just say the on time is exactly 1mS, but if it's higher I don't have to worry about it. Question is how to limit it to that 1mS.

    Just thinking out loud here, not even sure if I'm making sense.

    What are your thoughts? Thanks for the help so far, it's given me a different idea of how I might be able to do things.

  13. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    That sounds like too much work! The MIDI serial data already gives you the note, so you don't need to do calcs. You just need 12 constants, to represent the 12 possible notes.

    The MIDI data also gives you the octave, so you get the note from the lookup table of 12 constants, and divide it by 2 (or multiply by 2) for each octave.

    So note A1 is 440Hz, note A2 is 880Hz etc.

    I would also stay well away from periods, it's easier to work in freq (not period) and that avoids the nasty and slow 1/freq division math. :)

    Re the note Hi duty cycle needing to be <=1mS, that should not be hard. The note generation normally toggles the pin Hi and LO at twice the freq. So you toggle to HI at the correct time, but for the toggle to LO you include a "timeout" of X interrupts. A simple counter.

    So if the int freq is 19531 Hz, you always toggle HI->LO after 19 interrupts, IF the normal HI->LO note toggle has not already occurred. That gives you <1mS HI duty on low freq notes, and 50:50 duty on higher freq notes >500 Hz.

    I think for the moment you should try a single note generation, using the timer interrupt and get an understanding for the process and how you can refine ti to be fast and efficient. If you need to generate multiple simultaneous notes your code will need to be clean and very fast to execute.

    This is not an easy task! :)
    DerStrom8 likes this.
  14. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    This is not an easy task indeed :p

    After some research it looks like I can get the octave number by the following:

    note = MIDI.getData1();
    octave = (note / 12) - 1;

    Does that look about right?

    So from there, I guess it's just how to play the right note. I know you wrote it out in your replies, so I'll have to go through and re-read them.
    Last edited: Jul 28, 2014
  15. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    For the record, when I said I had a function to decode the note info, I was wrong. I have this:

    Code ( (Unknown Language)):
    1. uint16_t frequency[128] PROGMEM = {
    2. 8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16,
    3. 17, 18, 19, 21, 22, 23, 24, 26, 28, 29, 31, 33,
    4. 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65,
    5. 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123,
    6. 131, 139, 147, 156, 165, 175, 185, 196, 208, 220,
    7. 233, 247, 262, 277, 294, 311, 330, 349, 370, 392,
    8. 415, 440, 466, 494, 523, 554, 587, 622, 659, 698,
    9. 740, 784, 831, 880, 932, 988, 1047, 1109, 1175,
    10. 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865,
    11. 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960,
    12. 3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699,
    13. 4978, 5274, 5588, 5920, 5920, 6645, 7040, 7459,
    14. 7902, 8372, 8870, 9397, 9956, 10548, 11175,
    15. 11840, 12544};
    Many of those frequencies can probably be eliminated, but I left them in there because I'm too lazy to take them out :p

    Then, when I want to get the frequency from the MIDI note, I do the following:

    Code ( (Unknown Language)):
    1. note = MIDI.getData1();
    2. noteFreq = pgm_read_word(&frequency[note]);
    Then I can deal with the frequency itself, which is much more convenient.
  16. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    Just dividing the note number by 12 gives you the octave. The rest depends on what scale your base data (for your 12 notes) is in.

    That's pretty horrible too. ;)
    There's approximately a 6% change in frequency from any note (semitone) to the next semitone. So a +/-1% deviation in freq is a sixth of a note, which will sound really out of tune.

    With specifying integer note frequencies of <100 this will be really bad, and ideally for the notes to sound nicely in tune you need keep the note frequencies to +/-0.1% tolerance or better. So the constant that makes the note should be >1000.

    So with the note accuracy and the octaves, a good approach is to keep the 12 note frequencies as large numbers (with a lot of resolution) for the top octave, then for each octave down, divide that note freq by two. At the bottom octave the note value should be at resolution >1000.

    So given 8 octaves;
    A0 = 1760
    A1 = 3520
    A2 = 7040
    A3 = 14080
    A4 = 28160
    A5 = 56320
    A6 = 112640
    A7 = 225280 (this one is the constant in your 12 value array)

    So all your 12 note constants are up around 6 digit resolution.

    And assuming the interrupt is still at 19531.25 Hz, (10 mill / 512), then the code to generate an A7 note in the interrupt is this;
    Code ( (Unknown Language)):
    2. unsigned long bres;       // now using 32bit accumulators
    3. unsigned long note1freq;  // and 32bit note frequencies
    5. // this code is in main(), and sets the note to play
    6. note1freq = 225280;       // assign note A7 to note1
    9. // code below is in the interrupt
    10. if(note1on==1)            // this flag turns sound on/off
    11. {
    12.   bres1 += note1freq;    
    13.   if(bres1 >= 625000)     // using 19531.25Hz * 32
    14.   {
    15.     bres1 -= 625000;
    16.     PORTB.F1 = ~PORTB.F1;
    17.   }
    18. }
    So using the larger numbers means now the notes are accurate over lots of octaves, but the code is still very simple and fast to make a note. For each note there is a 32bit addition and a 32bit comparison, and on occasions a 32bit subtraction (when the note is toggled).

    That is still fast enough to generate multiple simultaneous notes in a timer interrupt that occurs every 512 instructions.

    Like I said, it would be good to strip your code down and just get it making one note, where you can set the note in code. Then you can make sure the note sounds good and is in tune (compare to a keyboard or PC piano freeware etc).
  17. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    Those are some great ideas. I think I see how things are supposed to come together. One thing that I'm still unsure about is the interrupt--is that an interrupt I attach to the timer1 module?

    I'm definitely going to open up a different project in Arduino to work on this by itself. It'll simplify things a lot, I think.

    Thanks again, I appreciate your help!
  18. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    By the way, here's a table of the MIDI note numbers. This is what I need to decode (come up with an algorithm) in order to find the correct octave/tone.


    This is what I was referring to when I said divide by 12 and subtract 1 to get the octave.

    However, it may be easier for me to simply use the formula:

    freq = 440 * 2^((n-69)/12)

    where "n" is the MIDI note number, to determine the frequency (based on A440 tuning). Then that frequency multiplied by 64 should give me a number in the range you suggested. Just as an example, let's take the note A3:

    MIDI number: 57

    Using the formula above, plug in 57 for "n" and I get 220 Hz (which is indeed the frequency of A3).

    Then I can multiply by 64 to get the 6-digit resolution, though if I use this formula I probably wouldn't even have to worry about that. Am I correct?

    Once that's done, then I have to worry about the duty cycle. Note1on (for example) is set by the MIDI NOTE_ON or NOTE_OFF command, so that's separate. I will need to use the timer to generate the tone at just the right duty cycle. Thinking I should save the duty cycle part for later though.

  19. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    Yeah I got that. :)

    To get the "note" you need the modulus of 12, ie; %12 in C code. To get the octave you just divide by 12 as integer math in C code. That is definitely the way i would do it!

    I got that you were referring to the official MIDI octave number but that is not very important. The octave number your code needs depends on what YOUR top octave is, and how you numbered them.

    For example the top 2 or 3 octaves in that chart are too high in pitch for your setup where you want to max out at 2kHz. And the lowest octave will be pretty useless too with a microcontroller squarewave output, and typical small amp and small speakers.

    At this point it would help a lot if we knew more about what the project is/does and how well it needs to perform. Like if you say now it needs to make high sound quality that changes everything. That will need something like a DSP micro or one 8bit micro for each note in the polyphony.
  20. DerStrom8

    Thread Starter Well-Known Member

    Feb 20, 2011
    Sorry about that, I guess I neglected to mention that this will eventually be used as an interrupter for a solid state Tesla coil. It will be applied to the enable pin of a MOSFET driver chip, which allows or prevents the feedback signal from re-triggering the coil. The schematic is as follows:


    The interrupter input is all the way to the left, and is fed through an optocoupler (for isolation) into the driver chips (2 paralleled UCC27425s) It occurred to me that in order to avoid the on-times from exceeding 1mS, all of the channels would have to be perfectly synchronized and none could overlap.

    As for playing the specific frequencies, would there be an advantage of calculating the octave and using the modulus to determine the note, rather than the formula I mentioned? It seems like the only downside to using a formula would be that it'll take a little bit of time to execute due to all the math, but I don't expect it would cause a problem. Then again, I'm no expert with Arduinos OR MIDI.... :D


    EDIT: By the way, I'm having some issues setting up the Timer1 interrupt. I guess I haven't done enough with timers yet to know how to do that.... :p
    Last edited: Jul 31, 2014
    THE_RB likes this.