Play it Again, Arduino

[MrRedBeard] wanted to play a particular song from an Arduino program and got tired of trying to hand transcribe the notes. A little research turned up that there was a project to convert Music XML (MXL) files to the Arduino. However, [MrRedBeard] wasn’t a fan of the language it used, so he created his own means of doing the same thing. He learned a lot along the way and was willing to share it in a tutorial that will help you if you want to do the same thing. You can see a video of his results, below.

Of course, MXL files are probably not better than sheet music if you had to create them by hand. Luckily, there’s a large collection of them available online and the song of interest was there. Note that the link in [MrRedBeard’s] post erroneously has the site as a .com instead of a .org, so you’ll want to use the link here instead of there.

The project that inspired him is on GitHub and uses Ruby if that suits you better. We’ve talked about MXL before, by the way. If you want to integrate multi channel music on the Arduino, you might start here.

Post navigation

11 thoughts on “Play it Again, Arduino”

Every application like MuseScore that can edit MusicXML files can also save standard MIDI files (.mid). Music XML files are meant to preserve the layout of the score on paper. Standard MIDI files only care about how it sounds (well, they do have some “useless” information like the time signature.

https://hackaday.io/project/24932-toy-synthesizer is inspired by the Commodore SID, and so far works on ATTiny85, ATTiny861 and Linux using libao. Should work well on the Arduino Leonardo as like the ATTiny861/85, has the high-speed PLL-driven PWM, and should be portable to other microcontrollers. (It is plain C.)

I’ve thought about expanding it to have it recognise the start of a tune, play a few notes, then flash the appropriate buttons in the sequence for the next few notes … but that’s a while off yet.

You really think an MCU running from an RC oscillator is going to be that accurate?

I can recall working on a video intercom system, and one of my coworkers trying to get the CODEC chip sample rate running at exactly 48kHz. He achieved it, then blew on the chip, the heat in his breath was enough to shift it by a few Hz. The system was running from a crystal and using fractional PLL dividers.

In the case of an AVR… you’re dividing a 8 or 16 MHz clock with an integer divider, so once again, even if you have a perfect oven-controlled crystal that is accurate to the picohertz… just how close do you think a $2 AVR chip is going to get?

Moreover, the human ear is not that good at sensing pitch in most cases.

The problem is not in the 16 MHz crystal. Even internal RC oscillator would be accurate enough.
As I mentioned above: the problem is with those integers:
For example ratio between E1 (defined as 41 Hz) and B1 (62 Hz) is 0.661 but ratio between F1 (44 Hz) and C2 (65 Hz) is 0.677. Both should be 2:3.
Only reasonable solution is completely ignore Arduino libraries and define own tone() function where parameter can be for example directly the oscillator divider used by 16bit timer in AVR.

So you’re going to quibble about a less-than ±1% error in frequency on a waveform generated from a square wave (thus, harmonically rich)? I might point out that as the frequency goes up, the percent error decreases.

To take your examples up a few octaves, E7 (nominally 2637.02Hz, rounded to 2637) and B7 (nominally 3951.07, rounded to 3951) gives a ratio of 0.6674259681093394, the correct ratio is 0.6674192054304277, the difference is just a smidge over 0.001%. Good luck picking it.

One way around this is to just multiply all frequencies by 8. E1 gets defined as 330 (41.25 Hz), B1 gets defined as 494 (61.75 Hz). The ratio of 41.25Hz and 61.75Hz is 0.6680161943319838, the error in the ratio is 0.2%.

I doubt most people will notice, and the aim here is to produce a noise in most cases, not produce a high-end synthesizer instrument for professional studio use. In the latter case, it is entirely doable to use a higher-end CPU to compute the samples, one which could do DDS down to the precision you require.

Some AVR parts only provide two hardware timers, the ATTiny series is one such example. Most used in Arduino projects also lack floating-point operations. The smaller ones don’t even do hardware multiplication.

In my project above, Timer0 is doing sample-rate timer duties, running at 8kHz or 16kHz… and timer 1 is doing PWM. Frequency is therefore derived from the sample rate in software. For a square wave, it computes using integer arithmetic, the number of samples it has to output before it flips the amplitude. Triangle and sawtooth waves, it has to figure out how many to increment the amplitude by, how often to increment, and when to reset the amplitude to the peak value so it can start counting again.

The ATTiny861 only does 8-bit integer addition, subtraction and single bit shifts, out-of-the-box. Want to divide by 8? Meet thy friend, Arithmetic Right Shift, you’ll need to call this instruction 3 times. Want to divide by 2^N? You’ll need a loop to call ARS N times. Want a value other than a power of two? Long division! Floating-point arithmetic? Be prepared for a math library to make an appearance.

All the while, this is going on, and there’s a deadline in which it must compute each sample. Sample rate of 8kHz means the CPU has 125ns to compute that sample, which equates to 2000 CPU cycles. It sounds like a lot, but it soon isn’t when you talk software float.

Arpeggio not song. Frequently heard in baroque music and some minimalist styles. It can now unfortunately be the entire “melody” in some rapping along with some farts and squeals.
Equal tempered is out of tune with it self. Using single digit integer values for tuning will give a natural tuning for a harmonic scale.