The Music File

The song is stored as an array of bytes in song.h. Here is an example:

const uint8_t song[] PROGMEM = {
         A3,     129,    D2,
         G4,     B4,     B1,
         Gb4,    A4,     129,
         E4,     G4,     129,
         E4,     G4,     B1,
         129,    129,    D2,
         129,    129,    129,
         Gb4,    A4,     129,
         D4,     Gb4,    D2,
         129,    129,    B1,

There are 100 possible notes which range from C0 (C in the 0th octave) to Eb8 (E flat in the 8th octave). The printer tends to freak out at the higher pitches, though. Flats are always used instead of sharps. There are 3 channels (3 instruments), each played by one of the printer's 3 motors. The first column is played by the X axis, the second column by the Y axis, and the third column by the Z axis. It is probably best to use the Z axis for bass or percussion because it is the slowest (lowest pitch) and most obnoxious.

Right now the timing is hardcoded at 48 beats/minute and 4 notes/beat. This means each note has a duration of 0.3125 seconds.

Each note (eg G4) is mapped to an integer using precompiler directives. The integers are the index for that note in the frequency lookup table. There are also some special notes listed below.

// Special Notes:
// 255 END OF SONG
// 128 STOP NOTE
// 129 CONTINUE NOTE

The Player

To start the song, send an M808 command to the printer.

The actualy music playing code is in music.cpp. The first thing it does is disable every feature in the firmware for making the printer run smoothly (feedrate, acceleration, and jerk limiting) and reduces the microstepping. After that it processes the song array 3 bytes at a time.

The real magic is in the play_notes() function. For each axis it calculates the length of the move based on the frequency and duration of the note. Then it uses pythagorean theorem to calculate the overal move length and feedrate, and lastly queues the move to the step planner.