Sorry, you need to enable JavaScript to visit this website.

Creating a simple synthesizer in Pure Data – Part I

Level: 
Tools: 

What is the objective of this tutorial?

In this tutorial we're going to make a very simple synthesizer using Pure Data and learn its concepts and workflow along the way. The objective is to get you started with the software in a kind of hands-on approach, doing something relatively useful, relatively quick. If you want in-depth stuff, there are some great sites on the internet. See pd-tutorial.com and the Pure Data FLOSS manual.

What is Pure Data?

Pure Data (aka pd) is a visual programming language for creating interactive multimedia works. It can be used to make algorithmic compositions, live performances, visual data representation, interaction with sensors and lots of other things. In this tutorial we will focus on audio synthesis.

What do I need to start?

To follow this tutorial you should have pd installed. Version 0.46.1 is used in this tutorial.

I recommend using it with JACK, because then you will be able to have low latency and (if you wish) connect the audio output of your patch to additional processing outside pd before outputting to the speakers. If JACK is already running, selecting Media→jack should be enough.

You will also need a way to input MIDI to your patch. If you don't have a MIDI controller, you can use some kind of virtual controller, like VMPK.

VMPK, a virtual MIDI controller

Remember that pd cannot receive JACK MIDI input, so use ALSA MIDI output in your controller (you can also use a2jmidid to be able to connect JACK and ALSA MIDI ports). Go to Media→ALSA-MIDI.

You can use a software like Patchage to manage both JACK and ALSA connections. VMPK can also connect itself to pd in Edit→Midi Connections.


Patchage showing both a MIDI keyboard and virtual keyboard connected up to Pure Data

Go to Media→Test Audio and MIDI.... This will open an interactive patch. Click the buttons under TEST TONES and you should hear some sounds. When you play something on your MIDI keyboard, you should see things happening in the MIDI IN section.

If something is not working, make some research on how to configure JACK in your system. This is outside the scope of this tutorial.

Getting started

When you open pd you will see a window where there's not much you can do. The DSP you see here stands for Digital Signal Processing and, as the name says, signals will only be processed when this is on. You will learn what a signal is in a second, but you should know that outputting sound depends on this, so, if you are having problems with sound, check if this is on before anything else. The white space below is a console where pd will output different kinds of information for the user (aka you).


Pure Data's main window

Click File→New. Now this is the real stuff. This window that appeared is your patch. It's where you will be getting all the work done. Programming in pd is done by placing objects on the canvas and connecting them, and that's exactly what we are going to do now.

  • Press Ctrl+1 and click somewhere to place the object, then write osc~ and click outside it to confirm. This object is an oscillator.
  • Repeat the previous step but write dac~ inside this second object and place it below the first one. dac stands for Digital-Analog converter (or Digital Audio Converter, it's not very clear). This is used to output sound.
  • Now press Ctrl+3 and place this new object above the osc~ object. Note that this one is a little different in shape. It will be used to set the frequency of the oscillator.


pd patch with your first objects

Object types

You have now created 2 types of objects: “Normal” objects and a Number object. The kinds of objects you will use the most when working with pd are Object (Ctrl+1), Message (Ctrl+2) and Number (Ctrl+3).

You can also use the Put menu to add all kinds of objects and see their keyboard shortcuts.

Inlets and Outlets

The little rectangles on the top and bottom of each object are inlets and outlets, respectively. To connect the objects, click and drag from one object's outlet to the others' inlet.

A mouse cursor change will tell you when you're clicking the right place, but in some systems the poor choice of mouse cursor may give you the impression that you are doing something wrong. Just ignore it.

Remember that you'll not be able to connect everything to everything. Some inlets expect control data and some expect signal data, concepts that you will learn right now.

Connect the output of the Number object to the left input of the osc~ object and the output of the osc~ object to both inputs of the dac~ object. You will see two different lines linking the objects. The thin line linking the Number object and the osc~ object represents a control data connection. The thicker ones linking the osc~ object to the dac~ object represent signal data connections.


Objects connected

Control Data and Signal Data

Control Data is sent from an object only when an event happens. A Number object, like the one you have in your patch right now, only sends the number as data when you change that number, or when it receives a bang message.

Bang messages are used to tell objects that you want something to happen in that exact moment, like triggering an object's action or a message. A bang message can be sent by clicking in a Message object with “bang” written on it or with a special Bang object (Ctrl+Shift+B).

While Control Data is pretty easy on the CPU, being only processed when ordered to, Signal Data is constantly being sent at a rate equal to the sample rate of your system. If your system's sample rate is 44,100 Hz (the sample rate of audio CDs) every Signal Data connection will have 44,100 numbers, or samples, going through it per second. This will only happen if DSP is on, so you can turn off DSP to save CPU power when it's not needed and to avoid undesirable sound while you edit your patch. All objects that work with signals have a tilde '~' at the end of their name.

Making some sound!

Now that the stage is set, we can use our patch to produce some sound. First we will need to switch from Edit Mode to Execute Mode, so we can control our Number object. Just press Ctrl+E.

Edit Mode and Execute Mode

Edit Mode is meant to be used only while you are editing your patch, as the name says. It will allow actions such as moving objects, connecting them and editing their contents.

Execute Mode will not allow editing, but will allow you to interact with the objects the way they were meant to, like clicking Messages to send them, using sliders, buttons, etc. So, when you're going to actually “use” your patch, you should always be in Execute Mode.

You know which mode you are in by looking at the mouse cursor. In Edit Mode it will be a hand and in Execute Mode an arrow.

Now make sure DSP is on, then click on the Number object and drag up and down to change its value. The value will be the frequency produced by the oscillator. While the value is smaller than 20 you will not be able to hear a thing, because human hearing starts somewhere around the 20Hz. After that the more you raise the value, the higher the pitch you hear will be.

Each inlet of the dac~ object represents an audio channel in your sound card. By default you will have two of them, for stereo output.

Anyway, now is as good a time as any to introduce you to Pure Data's help system. When you right-click any (or almost any) object in your patch and choose Help, it will open a new patch with information about that particular object. This is a patch like any other and you can interact with it to see how that object works. It's a really cool and useful help system. If you want to, you can open the dac~ help patch now and learn a little more about its usage.


Help page for dac~

But you might as well not want to do that, so let's move forward.

A tip for editing patches

Pure Data cares very little (or nothing) about helping you to make your patches look good. There's nothing to help you with aligning objects or spacing them adequately.

The best thing you can do to make things look something close to good is to select objects and move them with the keyboard arrows. To select an object without editing it, hold down the mouse button in some empty space close to it and drag the selection rectangle so it touches only the object(s) you want.

Each time you press an arrow key alone the object will move a pixel in that particular direction. Pressing an arrow key while holding down Shift will move it a bunch of pixels. I use this to align objects properly and space them equally, so it won't hurt my eye too much.

Making some useful sound!

Since what we want to achieve here is a subtractive synthesizer, even if I haven't told you so before, we will now ditch our oscillator in favor of another that creates lots of overtones. A subtractive synthesizer creates different timbres by taking a sound with lots of harmonics and selectively filtering some of them out, and we can't filter harmonics that do not exist.

Oscillators

Pure Data features two kinds of built-in oscillators: osc~ and phasor~.

osc~ will give you a sinusoid. A sinusoid is a pure frequency or pure sound, with no overtones.

phasor~ will generate a sawtooth wave, which contains a lot of harmonics added to the fundamental frequency (all of them, actually).

In Pure Data, osc~ will always oscillate between -1 and 1, while phasor~ will give values between 0 and 1.

Change your osc~ into a phasor~ (to do that you just need to rename it) and delete the Number object by selecting it and pressing delete.

Your MIDI controller will now come into play. Insert a new object and call it notein. This object will have three outlets. Insert three Number objects, remembering to deselect each one of them before inserting the next, or they will automatically connect. Then connect each of the notein outlets to one of the number objects.  If you play some keys on your keyboard and numbers appear inside the number objects, things are going well. If not, you need to find the problem and solve it before moving on.

You can remove these number objects at this point if you wish as they were just for testing.

In a notein object, the first outlet from the left will give us the MIDI number of the note you played. The second will give the velocity and the third will contain the MIDI channel. If you want to hear the sound of the note you played, you need to somehow connect the note's frequency to the phasor~ object. Since the notein object is not giving us a frequency, but a MIDI code, we need to make a conversion using the mtof object.

mtof and ftom
These are easy. mtof will convert from MIDI value to frequency and ftom will do the opposite.

So now we must connect the left outlet of notein to mtof and connect mtof to the phasor~ object's left input. Now when you play a note you should hear the correct pitch, but it will never stop, and it will be loud and it will hurt your ear pretty fast. Don't worry, we'll fix that soon enough.


Sounds like crap and never stops

Let's start by adding a volume control using an arithmetic operator with a slider.

Arithmetic operators

If you insert an object with one arithmetic operator, it will allow you to do calculations between two numbers or operands. The two inlets are the left and right operands and the outlet is the result.

A lot of operators are available, but we will use the * (multiplication) operator for now. A * object will multiply only Control Data. If you want signal multiplication, use *~.

If you want you can put a default value for the right operand by passing an argument to the object.

Object arguments

Arguments are values that are passed to objects by writing those values after the object name.

Remember to always have a space between the object name and the argument and also between arguments, if there's more than one.

In any arithmetic operator object, for example, the argument will be a default value for the right operand. Check each object's help patch to see what arguments can do for that particular object.

Create a signal multiplication object, *~, and connect the output of your phasor~ to its left inlet. Remove the phasor~'s connections to dac~ by clicking the connection line itself and pressing delete. Then connect the *~ object's outlet to both dac~ inlets, just like the phasor~ was connected before.

Now let's create a special object: a slider. You can create a horizontal slider with Ctrl+Shift+H and a vertical one with Ctrl+Shift+V. You can choose the one you prefer and, after placing it, connect its outlet to the right inlet of the signal multiplication.

Every number that comes from the oscillator will be multiplied by the value of the slider before it reaches the speakers. To configure the numbers that the slider will output, right click it and select Properties. Set left to 0 and right to 1 (bottom and top if you choose vertical). Since you're already at it, set the Label to “Volume”.

Now, just by sliding this in Execute Mode you can control the output level of your patch.

Still crap, but now under control

Now we need to be able to stop the sound when the key is not being pressed anymore. The way it works is: when a key is pressed, you get a velocity value greater than zero. In a real MIDI keyboard this value is higher the faster (harder) you press down the key. When a key is released you get a note off signal, that is actually the same as a key press, but with velocity 0.

Since, for now, we are making the most simple synthesizer possible, we will do everything in the simplest way. We will just project the velocity to a value between 0 and 1 and multiply the signal by this.

Start by breaking the connection between the phasor~ and the multiplication that you already got there and create a new signal multiplication object (*~) between the old one and the phasor~. Connect the result of this new multiplication to the first inlet of the old one and connect the phasor~'s output to the new multiplication's left inlet.

Since in MIDI velocity values go up to 127, we'll just divide it by 127 to get the value between 0 and 1 that we need. Add a division by 127 object, / 127 (remember to have a space between “/” and “127”, since “/” is the name of the object and “127” is the argument) and connect the second outlet of notein (the velocity value) to the left input of this division.

Now you can connect the output of this division to the right input of the multiplication you just created and voila. You should now have sound only while you are pressing a key in your MIDI controller. Remember to put some volume using your slider or nothing will come out of the speaker.

Almost there

You may notice pretty soon that this cheap solution will break as soon as you press more than one key. The first problem is that you have just one oscillator, so there's no way to produce two notes. This is not really a problem, because mono synths do exist! The real problem is that when you release the first note, the second note will stop sounding and that's not the desired result.

To solve this problem we're going to use a set of objects that, at first glance, will look kinda overkill. After seeing the possibilities with this new setup you'll certainly understand the reason why I had to introduce you to all these new objects. They are: poly, pack, route and unpack.

poly

The poly object can take the pitch and velocity from your notein object and add a voice value to it, outputting those three values.

Using the voice number you will be able to handle each note  separately, eliminating the problem of one interfering with the other and allowing you to make a polyphonic synth!

The first argument you give will be the number of voices and the second needs to either 1 or 0 to, respectively, allow or disallow “voice stealing”.

With voice stealing, when all voices are occupied a new note played can take one of the voices to itself.

pack and unpack

pack will allow you to take a lot of inputs and then output one single list that contains all values. This is useful to create valid input for objects that expect to receive lists on their inlets, like route.

unpack will convert this list back to multiple outputs. The number of inlets of pack and the number of outlets of unpack will depend on the number of arguments we pass.

If you pass a number as one of the arguments, the respective inlet will expect a number and will be initialized to the value you passed. So, if you pass 0 0 0 as arguments to pack, the object will create three inlets, each of them expecting a number and initialized to zero.

route

The route object will receive a list, like the one produced by pack, as input and will output the contents of the list to different outlets depending on the first value of the list.

The number of outlets will equal the number of arguments plus one. An object created as route 1 8 3 15 will have 5 outlets.

Every list starting with 1 will go to the first outlet., with 8 to the second outlet and so on. The fifth outlet will get all lists that start with a value that was not given as an argument. The output will not contain the first value, that was used only for routing.

Let's do it, step by step:

  • Disconnect the notein object from the mtof and / 127 objects and open up a lot of space between notein and the rest of the patch.
  • Insert a poly 1 1 object (a poly object with 1 1 as parameters, to be more correct, but I'll use this kind of notation to keep it simple) and connect the two first outlets of notein to the two inlets of poly.
  • Insert a pack 0 0 0 object and connect each poly outlet to the respective pack inlet.
  • Insert a route 1 object and connect the pack outlet to the left route inlet.
  • Insert a unpack 0 0 object and connect the left route outlet to its inlet.
  • The unpack object now must connect like notein was connected before. Connect its left outlet to mtof and its right outlet to the division by 127.

There! ...but still crap

We're done!

You should now be able to play your synthesizer with no problems. I would not even call that a synthesizer yet, but we are getting close. In the next tutorial we're going to add an ADSR envelope to it and will also allow polyphony.

Written by Eduardo Mezêncio