Arduino music from a midi file
Recently I wrote an Arduino / AVR lib and python script so that I could take three parts from a midi file and playing them simultaneously using the three Arduino timers.
There are already plenty of other ways to generating tones using an Arduino. You can play music from a midi input there is a library for playing PCM audio and similar projects for playing music using all three Arduino timers simultaneously. I’ve also played around with AVR sound generation before in previous posts.
Note: if you just want to use the pre-generated header files in the songs/ directory you can download the lib and hook up your Arduino to a speaker to play tunes..
What you need to get started
- Arduino (or an atmega328, atmega168)
- A speaker and some potentiometers to mix the inputs.
- A midi file you want to convert
- Python and the beautifulsoup library for parsing xml
- Sequencing software like the Musescore (free) that will also convert the midi file to musicxml
- “xml2h.py” which parses a musicxml file and generates a header file with notes and delays for the arduino
- PlayTune library that will play the song on the atmega for any tune for up to three tracks simultaneously.
The result..
How music is generated using an Arduino
On the Arduino and in general on the atmega168 and 328 there are 3 timers that operate independently of each other in hardware. The timers count up (starting at 0) to either 255 or 65535 and reset to zero. When these timers are put into “CTC” (clear on timer compare) a pin is toggled every time the counter matches a specific value called the “match value”. The speed of the timer depends on two things, one is the speed of the clock ( for the Arduino this is 16MHz ) and a clock prescaler. The “prescaler” is an integer that can be set that will slow down the timer by a factor of 1024, 256, 64, or 8. Once the timer is setup in CTC mode and a match value is specified there will be square wave generated on a pin at a frequency proportional to how frequently the timer hits the match value.
Here are the match values that correspond to different pre-scalers and their corresponding piano notes at 16MHz. (This is from a spreadsheet I made which comes in handy especially when dealing with different clock frequencies)
Let’s start making music..
Using the PlayTune library
Copy playtune.cpp and playtune.h to a “PlayTune” directory in the libraries folder of your Arduino install. Once it’s copied there you should see it in the “import library” list under “Sketch”.
To initialize PlayTune pass into the constructor what timer you want to use, what prescaler, a list of match values and a list of corresponding delays.
- “isPlaying()” lets us know if there are still notes left to play for the part. When it’s finished it will reset back to the beginning.
- “playNote()” will iterate over the note list, playing the tone that corresponds to the match value or a rest if zero.
By default the PlayTune lib will play a scale from middle C to tenor C if you just pass in the timer you want to use.
In this example we will play the scale on all three timers simultaneously.
(connect your speaker to pins 6,9 and 11)
#include
#include
void setup(void)
{
PlayTune p0(0);
PlayTune p1(1);
PlayTune p2(2);
while (p0.isPlaying() || p1.isPlaying() || p2.isPlaying() ) {
p0.playNote();
p1.playNote();
p2.playNote();
_delay_ms(500);
}
}
void loop(void)
{
}
Note that the _delay_ms(500) function requires the additional include of util/delay.h.
Do not use the Arduino “delay()” function! It will cause problems because it uses TIMER0 which is being used by the lib for generating the tone.
So that was fun (but boring).. Now on to creating some more interesting music…
Midi, musescore and xml2h.py
Musescore is a wonderful free sequencing tool. Using that you can open midi files, adjust them, and convert them to musicxml.
“xml2h.py” is a script I wrote that takes a musicxml file and generates an header file that tracks two arrays for up to 3 parts, one for note values and another for delays (rests are note values equal to zero)
The conversion from midi to arduino tones is a bit of a hack :/
xml2h.py will do its best to:
- pick the best prescaler
- ignore chords
- combine voices if it can
- handle tied notes
I’ll walk through a video game theme as an example (it works well since the songs are simple). There are a number of sites out there with video game midi files most of them look like they were designed in 1997. Below I’ll use the Super Mario Brothers theme. In this score there are 4 parts, we will convert the first three to play on the Arduino.
There are some limitations to keep in mind for the midi conversion:
- If notes span multiple clefs then you there will probably be too large a range for the resolution of the timer, not many (simple) songs have this problem though.
- When you convert you will need to select between 1-3 parts to be played simultaneously.
- Chords will be ignored by the xml2h.py script.
- If there are more than voice on a part the script will try to merge them together.
I wrote a plugin called “chord zapper” for musescore that will (attempt to) remove all the chords. Copy this to the musescore plugin folder, select all, run the plugin to get rid of all the chords if you want to get a better sense of how the song will sound when converted. Whether or not you remove chords the “xml2h.py” script will ignore them, taking the top note in the chord if there are any.
When you are satisfied save the midi file save it as a “MusicXML” file and run it through xml2h.py.
The script takes the xml plus the name of the header file you want to write out.
For example:
Pick the clock frequency. This defaults to 16MHz which is what you want for the Arduino
Pick the part you wish to process (part0 corresponds to the top clef in the midi file)
Pick the prescaler, the script will give you a subset of prescalers, you should select the lowest one that is supported. (see note on prescalers below)
If it finishes without error you will end up with a header file that contains some arrays and macros for passing them into the PlayTune constructor.
Here is what the resulting header looks like, the macros for PART1, PART2, PART3 are used in the sketch below:
#include
#include
#include
void setup(void)
{
PlayTune p0(0,PART0);
PlayTune p1(1,PART1);
PlayTune p2(2,PART2);
while (p0.isPlaying() || p1.isPlaying() || p2.isPlaying() ) {
p0.playNote();
p1.playNote();
p2.playNote();
_delay_ms(50);
}
}
void loop(void)
{
}
Assuming you are converting two parts and the first part will be using Timer0 and the second part Timer1 pick the lowest value from the two sets that show up in the prescaler selection.
The only difference between this sketch and the last one is the additional include for the “smb.h” header file, the addition of the PART# macros used in the PlayTune constructors and a smaller delay.
The value of the delay will determine the tempo of the song and corresponds to the delay between each tick in the MusicXML file or the shortest note. Usually this is either 1/32 or 1/64.
If you have questions about the lib or if you create more songs let me knowand I will add them to collection.
Selecting Prescalers
In general you want to select a low value for a prescaler to give you the highest resolution (to match the pitch of the song as close as possible).
The two timers on the attiny support different prescalers though so you need to be aware of what timer you are using (0 or 1) and what prescalers it supports.
Timer0 supports: 1024, 256, 64, 8
Timer1 supports: 1024,512,256,128,64,32,16,8
Filed under: Uncategorized | 12 Comments




I did this a year ago… except I used a frightful combination of processing and 10 year old midi programs to get the same effect. Needless to say, your implementation is much, much better. Thank you!
Just as an fyi, when doing this with an Arduino UNO, I had to go through and replace your PD# and PB# port references with PORTD# and PORTB#.
I also had to include to be able to compile in the Arduino IDE 0021.
At any rate, this is awesome work. I’m using it to make custom Christmas cards this year. Thanks for sharing!
As a follow-up, I have hosted my working code for the Arduino UNO, the modified library, and a single song here if anyone needs/wants it: http://goo.gl/0mryF
Thanks! I’ve incorporated those changes so in theory it should work with the Arduino Uno as well as the attiny85.
-John
Hi,
will it work on a ProMini 16mhz ATmega 328?
I’m a total noob, what is the use of the potentiometers?
Thanks
BTW can you put a schematic also?
Thanks.
That should work. The pots are to control the volume of the two signals going to the speaker.
Cool thanks!
Is there a way to play and pause the music and re-play ?
This is proving to be very difficult to follow. I am not familiar with python so when you say stuff like “run it through xml2h.py”, I have no idea what to do.
Hello!
Can you provide the scheme of your sketch? It’s hard to me to understaind it by watching your video.
Thank you!
Hi, recently i sent to you an email asking you for a questions. Actually i am using the serial port to read midi info, but now i want to read midi from a midi file. The only parameters i need are: midi channel, note on/off, and velocity… i think this should be pretty simple, but i do not know the steps i have to do… Could you help to me?? Thx!!!