Wednesday, March 2, 2016

Bash script for converting sheet music into 3D printer G-code

In my last post, I posted G-code for the startup and shutdown tunes I play on my Rostock Max v2 3D printer.  Those were proof of concept, manually computed using a spreadsheet.  However, once I started, I saw that it would be easy to write a short script to automate turning sheet music into the required G-code to make the printer play music.  Before I explain how it's all done, here's a little of the Tetris Theme to whet your appetite (full explanation and downloads start after the jump):


3D printers are made to print objects, not play music.  Using a 3D printer to play music could cause damage to the printer.  Use this code at your own risk, the author assumes no liability for any damage that may be caused by its use.

How it works

In figuring out how to make my printer make music, I found the following blog post highly information and would have been lost without it: In short, the author figured out how to make music using a CNC mill.  The trick was to adapt his approach to work on my 3D printer.

Fundamentally, in order to play a music note, you need to generate the specified frequency for the specified amount of time.  As an example, a middle C (C4) quarter note played at 240 beats per minute requires you to generate a 261.63 Hz tone for 1/4 second (see here and here for details on that calculation).  The good news is that rotating a stepper motor at 261.63 steps per second for 1/4 second will generate the desired tone.  The bad news, G-code doesn't take frequency or duration as arguments.

HIGHLY SIMPLIFIED DISCUSSION OF HOW 3D PRINTERS WORK:  In order to print a 3D object, your print software slices the 3D object into a series of 2D cross-sections.  These 2D cross sections are then used to prepare instructions (referred to as G-Code), that tells the hot end how to make the 2D cross section by drawing lines made of extruded plastic filament.  My printer uses Reprap G-code, the reference for that G-code implementation can be found here.

There are two G-code instructions we're going to focus on, G4 and G1.  G4 is straight-forward, this command pauses the print.  It takes one argument, the number of milliseconds that the printer should delay before executing the next instruction (so G4 P500 tells the printer to delay 500 milliseconds, or 0.5 seconds, before moving on).

G1 moves the printer head to create your 3D object.  It takes the following arguments:
G0 Xnnn Ynnn Znnn Ennn Fnnn Snnn
The X,Y, and Z arguments specify where in 3D space the hot end should go to.  The F argument specifies the speed (in millimeters/minute) that the hot end should travel when headed to the desired location.  For the purposes of this project, we're going to disregard E(extrusion) and S (stop check), as they are not relevant to what we're trying to do.

My printer has three motors, so technically, I should be able to produce three different notes.  However, a) the math is much harder and b) each note is already quiet enough as it is.  So, to simplify things, I chose to turn all three motors at the same rate to produce one, louder note.  With my printer, moving the Z-axis is the way to move all three motors at the same speed.  So, for the purpose of this project, we'll be disregarding the X and Y arguments as well.

Now we have two parameters we need to set to generate a note (steps/sec and duration), and two parameters that we can send the printer in G-code (speed in mm/min and Z-distance).  Believe it or not, at this point, this is really just a unit conversion problem.

Steps/second can be turned into mm/min by the following method.  Translation from seconds to minute is easy -- just multiply by 60 (i.e., there are 60 seconds in a min).  That gives me steps/min.  now I just need to know how many steps there are per mm.  Looking through the firmware source code for my printer, I located exactly what I needed -- STEPS_PER_MM=80 (if you're looking for a different printer, carefully parse the file, as there are multiple STEPS_PER_MM definitions, only one of which applies to your printer.  So dividing steps/min by steps/mm yield mm/min, the number we need for the F argument.  To reduce all of that math, for my printer, you multiply the desired frequency by 3/4 in order to get the F argument.

The Z-axis argument is given in mm, yet I have duration (in seconds).  Fortunately, I just computed a quantity that relates the two, speed.  My speed was mm/min.  If I divide by 60, that gives me mm/sec.  Multiplying by the desired duration yields a distance, in mm.  That is the change in my Z-axis.

It looks like I'm done, but there is one more wrinkle.  The Z-axis argument for G-code works in absolute position, but I just computed a relative position (i.e, how far I need to move from where I currently am).  In order to fix that, I just need to track my position from step to step, so I know where along the absolute axis I need to be to move the correct relative distance.

My code

To demonstrate, I created a bash source file that will take notes and durations and turn them into G-code.  You can get it at the following location:  For Mac and Linux users, you just should be able to clone my repository and just start working with it.  Windows users, if you really want to make this work for you, install cygwin.

How to use

In short, you are creating a bash script that you will then execute to generate the G-code.  You'll can then cut and paste the G-code into your printing software to use it.  For example, pasting the code into a macro would allow you to play your song at the press of a button.  

The top of the bash script must include the source file in order to function.  Everything from there on out is a variable.  $START and $STOP variables are provided in order to produce startup/shutdown G-code for your script.  To play notes, just specify the note and duration.  

For instance, to play a C4 half note, you would enter:
    $C4 $HALF
A G#5 eighth note would be:
If you don't specify a duration, a quarter note is assumed.  So, a E3 quarter note would be $E3

Once you have your script complete, save it and make it executable (chmod +x ./  To run, just type in the script name (i.e., ./  It should start spitting G-code out to the console.  Then it's just a matter of cutting and pasting that code into your printer software to get results (how to do that is a bit beyond this post, as there are a multitude of software tools that all operate slightly differently).

The source file is heavily commented to assist in understanding how things are coded.  Similarly, four commented music scripts are included in the examples directory to demonstrate how to generate your own.  Please take a look at those, hopefully they answer any remaining questions you might have.


If you've made it this far, congratulations.  As a reward, I've included a playback of one of my example files (The Overworld Theme from Super Mario Brothers).  Enjoy!  If you have any questions/comments, please be sure to put them in the comments below.

No comments:

Post a Comment