Printesizer - a 3D Printer Synthesizer

Introduction: Printesizer - a 3D Printer Synthesizer

About: Software developer by day, sleeping person by night. Other stuff in between.

Since the first time I entered a roomful of 3D printers all working at the same time, there seemed to be something musical about it. The different pitches would sometimes create a melody, a repeating pattern turn into a rhythmic groove. I wondered if there could be some way to harness their peculiar sound as an actual instrument.

The opportunity came at a 3-day hackathon organized at Autodesk, my workplace, to experiment with this idea. My research brought me to this code for converting a MIDI file to GCode. While checking that this worked with one of our 3D printers was straightforward (and very exciting to hear it for the first time!). I immediately started thinking what more can be done with this. And of course, more == more printers. Since each printer can move in 3 axis, that means you can play at most 3 notes at the same time. Adding more printers then means adding more polyphony. Or being able to conduct a printer orchestra.

After achieving this, there was something else missing. Playing existing music files was fine as it was, but what if you could play the printers as if they were an instrument by itself?

The result was presented at the end of the hackathon, and you can see it in action here:

If you want to understand how it works read the following explanation, otherwise you can skip to the first step.

How it works

The idea is actually very simple: a 3D printer uses stepper motors to move its head and bed, using a language called GCode. A GCode command looks like this:

G01 X10.0 Y5.0 Z0.0 F2000.0

where G01 means move in a straight line by 10.0 units in the X direction, 5.0 units in the Y direction, with a feed (or speed) of 2000.0 units per second.

What changes the pitch of the sound is the speed – a higher speed will produce a higher pitch and viceversa.

To play a MIDI note, on the other hand, we would need the note name and duration, e.g. A4 (A on the fourth octave) with a duration of half a second. Each note has a corresponding frequency – see this page for a table and formula showing how to convert between them. The frequency is the speed at which a string or membrane would need to vibrate to produce that particular note.

We can then translate a MIDI note to a GCode command by using an elementary formula:

distance = speed * time

We need one more parameter, though, which is how many steps the stepper motor has to make to advance the head 1 millimeter, say 50 steps per millimeter. This value varies with each 3D printer.

Going back to our example, A4 would be 440 Hz (steps per second) and time 0.5 seconds.

The feed is expressed in mm / minutes so multiply by 60 to get from seconds to minutes:

speed = 440 [steps/sec] * 60 [sec/min] / 50 [steps/mm] = 528 [mm / min]

The distance will then be:

distance = 528 [mm/min] * 0.5 [sec] / 60 [sec/min] = 4.4 [mm]

Finally then, sending this command would produce the desired note:

G01 X4.4 Y0.0 Z0.0 F528

Furthermore, by combining a different movement in X and Y, we can produce 2 notes, since each motor will be moving at a different speed to produce the desired final speed (I'll leave as an exercise to find what the speed should be in that case. Hint: Pythagoras). You could use the Z axis as well for a third note, but in my case I didn't use it, since the Z axis movement is usually slower than the other motors.

Moving to real-time

So far, so good when playing a MIDI file, but there's one problem when you want to play in realtime: you don't know what the duration of the note will be! The only thing you know is when a note is being played (a NOTE ON event) and when it stops (a NOTE OFF event).

To solve this, we can slice the note into what I called micronotes. Take the shortest duration your printers can take (in my case it was 50 milliseconds), without the printer's buffer filling up too fast, and send the same GCode command repeatedly as long as a note is on. When the note off event is received, stop sending the command.

That's it for the intro. We can start tinkering!

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

Step 1: Gather All You Need

  • 1 or more 3D printers (only open printers that can be controlled through a network)
    • The printers I used were a Z-Morph and a Monoprice. Some printers can't be controlled through a TelNet connection, such as the Makerbots post-2012 models (when they became more proprietary and closed source)
  • MIDI controller
  • LAN router (or your existing wireless connection if your printers support WiFi)
  • Computer to run the python code (get the code at the Printesizer's repository)
    • You can use a RaspberryPi instead to make the system more mobile

Optionally, you can use the following to amplify the sound (although I very much recommend it)

  • Some cheap earbuds (1 for each printer)
  • Masking tape (you ALWAYS need masking tape)
  • Audio interface or amplifier. I used an M-Audio Fast Track Pro, a low-cost USB Audio interface with 2 inputs.

Step 2: Putting the Hardware Together

Network

Connect your laptop and printer to the same network. Some printers have WIFI, others only a wired connection. Either should work fine (I had one of each in the video). You will need the IP address of the printers, to add to the python script later. Check with your printer's guide on how to get it. Usually you can find it through the printer's display. You can see here an example on how to connect and get the IP for the ZMorph printer.

MIDI

The MIDI controller will be connected to your laptop. I used a Novation Launchkey Mini, but any MIDI keyboard will do, even an old synth with old style MIDI outputs (as long as you have a way to connect it). There are MIDI to USB adapters you can use in that case. Some Audio Interfaces also have MIDI input. Most modern MIDI controllers are USB already, so in that case there's nothing else to worry about.

You can open the diagram above to see tooltips with further explanations.

Step 3: Amplification

Amplification can be a bit tricky. My first attempts were with all kinds of good quality microphones, both dynamic and condensers. None were particularly well suited for the task, because the whole printing environment is inherently very noisy.

In the end, a very cheap solution provided a much better result. It was the realization that headphones can be used as microphones. They are built with the same principle, but in reverse: in a microphone the movement of a membrane is translated into a voltage, while in the headphones the voltage is translated into a movement of a membrane. Because earbuds are very small, we can attach them more closely to the area that is actually producing the sounds: the stepper motor.

Test the placement by attaching the earbud to different locations in the stepper motor, like pictured, in order to find the sweet spot that produces the best sound.

Step 4: Amplification - Fixing It in Place

Once you find the spot where you can hear the motors best through speakers, attach with a good dose of masking tape, and connect them to your amplifier / audio interface. I recommend playing a sound constantly while attaching with the tape, because even a slight variation in the angle of the earbud can have an effect on the loudness you achieve.

I used an M-Audio Fast Track Pro interface to send the sound from the earbuds to the computer. This allowed me to add a compressor effect, which boosts the sound even further. But if you only have an amplifier or a stereo equipment with a microphone input, you would just connect it directly and that's it.

Step 5: Getting the Software Ready

First, get the code from the Printesizer repository.

Then, make sure you have Python installed, as well as pip (the Python package installation manager). You will need the rt-midi library to process MIDI messages. To install it run:

pip install python-rtmidi

Step 6: Getting the Prompt

Some printers, when making a telnet connection, output a message prompt. In my experience (at least with one of the printers -- the Z-Morph) it is necessary to read this prompt and discard it, otherwise the printer wouldn't receive any commands until I did so.

I prepared a python script that you can run to find out what the prompt is. It's in the same repository. Just run

python readPrompt 192.168.1.10

where the last part would be the IP of your printer. If after a few seconds it looks stuck, it's because there's no prompt. Just press Ctrl-C to exit.

In case there is a prompt, you will use it in the next step.

Step 7: Customizing the Software

Now, you will need to customize the code for the printers you will be using. Take a look at the machines dictionary at the beginning of the code:

machines = {
	'zmorph': {
		'ip': '10.0.0.10',
		'safemin': [ 0, 0, 0 ],
		'safemax': [ 225, 50, 200 ],
		'ppu': [ 76.19, 76.19, 1600 ],
		'prompt': 'Smoothie command shell',
		'end': '',
		'resetCommands': [],
		'noterange': [21, 108],  # A0 to C8
		'higherNotesPriority': False,
		'transpose': 12
	},
	'monoprice': {
		'ip': '10.0.0.11',
		'safemin': [0, 0, 0],
		'safemax': [120, 120, 120],
		'ppu': [46.50, 46.50, 548.75],
		'prompt': '',
		'end': 'M77',
		'resetCommands': ['M201 X1000 Y1000 Z100', 'M203 X6000 Y6000 Z300'],
		'noterange': [21, 108],  # A0 to C8
		'higherNotesPriority': True,
		'transpose': 0
	}
} 

You can add as many machines as you like here. The parameters are:

  • ip: The IP address of the printer in the network.
  • safemin and safemax: The limits where the printer head can safely move without reaching the sides of the printer, in millimeters.
  • ppu: pulses per unit, also known as steps/mm. That's the amount of steps the stepper motor turns to create a movement of 1 mm. You should check what the values are for your printer. If you get these values wrong, the notes will be off key. Check here for how this usually looks like.
  • prompt: Some printers will output a string when you first connect to them. By putting that string here, it will be read and discarded, otherwise the connection can get stuck.
  • end: the printer command to stop a print job. Not mandatory, but a cleaner way to disconnect for some printers.
  • resetCommands: any extra commands needed to initialize the printer.
  • noterange: if we want the printer to receive only certain notes, we can filter the range here. The values here are MIDI notes, so for example an A4 note is MIDI note 69, A#4 is 70, B4 is 71, etc. This can allow distributing the note ranges between different printers (some printers might sound better on the higher range than the lower range).
  • higherNotesPriority: normally, when playing more than one note, they are ordered from low to high, and sent to whichever motor is available. So if you are playing 3 notes, but only have 2 motors available, the higher note will not be played. Setting this parameter to true reverses the order, so the lower note will not be played.
  • transpose: changes the pitch, in semitones, e.g. setting this parameter to 12 will transpose the notes played by an octave up.

Once you have set up your printers, search for the following lines:

zmorphMachine = NoteToGCode('zmorph') 
monopriceMachine = NoteToGCode('monoprice')

This initializes the printers. Change the names according to the ones you set (and add more lines if you have more printers).

Finally, in the loop below, near the end, you will also have to change the names (and/or add more lines) where the notes are sent to the printers:

zmorphMachine.sendGCode(active_notes)
monopriceMachine.sendGCode(active_notes)

You can also search for this line:

microNoteDuration = 0.05 # in seconds

and try changing it to something shorter. The longer this duration is, the longer delay you'll have between the time you stop playing a note, and being able to play the next one.

That's it! The code should be ready to use. If you find any problems, you can open an issue in the repository.

Step 8: Run the Code

Run the python code:

python main.py

You will get a list of MIDI connected devices, so choose the controller you want to use from there. You can note the number in the list, and run directly with the number as a parameter next time (provided no device is connected/disconnected in the meantime, the list order shouldn't change). So for example, if your controller is number 1 on the list, run:

python main.py 1

Step 9: Play to Your Heart's Content!

It was that simple, now start playing some tunes!

Try adding more printers, add some effects when amplifying. It's possible that piezoelectric microphones (the kind that are attached to acoustic guitars, for example) could work as well (or better) than the earbuds, but I haven't tried that yet.

I'm sure there's much more that can be done to improve this project. To get some ideas, check the spiritual successor, the Fabulator.

Instrument Contest

This is an entry in the
Instrument Contest

Be the First to Share

    Recommendations

    • Magnets Challenge

      Magnets Challenge
    • Snow Challenge

      Snow Challenge
    • Wearables Contest

      Wearables Contest

    2 Discussions

    1
    audreyobscura
    audreyobscura

    21 days ago

    This is a super cool concept! Do you have any more photos on some of the assembly of your amplification set up? Could you share a circuit diagram?

    0
    elifer5000
    elifer5000

    Reply 18 days ago

    I'm glad you liked it, and thank you for your feedback!
    I added a few more pictures of the amplification. Let me know if it's clearer now.