Also from Andrew Faraday

Where else can you find me?

Sunday, 9 June 2013

Putting Pure Data in a box (or: Pure Data Hiding)


I've had a couple of weeks off the blogging, so it's time to get started again. You may not know that I'm actually something of a data nerd and tend to get very interested in figures and statistics that are drawn from my life in some way (which is actually quite a good way to go about losing weight, by the way). So I've decided to try and draw a topic from the statistics on this blog. So first, I'm going to share with you the list of search terms google tells me are being used to find it...

Well, the first thing to note is that it's mostly looking for me in some form or another (although I've never been to Toronto), and the most searched thing that's brought people to my blog is this phrase; "Pure Data Hiding"). A quick google search reveals, also, that my blog is several pages in, and while there are various discussions, forum topics, plugins and technical information. It seems people are skipping through it. My guess would be this is because they're not finding the kind of information they want.

So I'm thinking it's time to make good on my recurring promise to blog a little bit about hiding away parts of our pure data patch we're not presently interested in, leaving us free to concentrate on the visible elements of the patch.

In order to show why we would hide parts of Pd patches away, let's try an exercise in "Putting it all together":

Step 1: Pick some components

I often stress that what I'm teaching here are algorithms that can be re-used in different ways, and effectively become parts of a recipe we can vary and put together differently, so, looking back at existing blog posts, let's take this rhythm generator from Pure Data and BACON (Part 1):

The Step sequencer from from the end of Sequencing and Wavetables, Part 1: Arrays in Pure Data

And The complex synth from the end of Algorithms of the Street: Part 2 - Amplitude Modulation:

Note: I would recommend, if you want to understand the patches used in this example, you read the posts they're from first. If not, you can take my word for what they do.

Now, we've got three distinct components here, the first is responsible for the placement of notes in time (rhythm), the second, selects pitches to be produced and the third changes these pitch numbers into a rich synthesized tone. So there's no reason they shouldn't just be chained together to produce a patch which plays the same sequence of notes with the same rich tone with a randomly generated rhythm.

The problem is, I'm using a standard size of patch, to display on my blog, and even if we removed this restriction, the height of this patch wouldn't fit on my screen. We need a way to lock these things away. But, as always, we start with baby steps.

Step 2: A sub-patch

Let's take that first, rhythm generator patch and hide it in a sub-patch:

You can already see that it's not taking up nearly as much of the screen. However, you can also see that you can't see the rhythm generator itself. If you're coding along, you will also have noticed that when you create the object (pd rhythm) it will have opened an empty Pure Data window, called rhythm. This is what's called a sub-patch which is a patch within a patch, it's basically part of our pure data file, but it's not shown in the same window. You may also notice, that it has no inlets or outlets when it's created, making it impossible to connect to the click~. So our first job is to create one...

So, we create an object called outlet, inside the sub-patch, rhythm like so:

And the second we do that, the object (pd rhythm) on the parent patch gains an outlet so it looks like this:
There's no co-incidence here, the outlet inside our sub-patch directly maps to the outlet shown on the pd object. Anything that goes into the outlet object, will go to the outlet of the pd object. So all we need to do now is build our rhythm generator inside the sub-patch and connect it up to the click~, outside it.

If you've read Pure Data and BACON (Part 1), you'll know that this is sending out bangs (the Pd activation message) at a changing set of intervals, based on fractions of 1 second. You'll also know that this needs input from a toggle to start and stop the process. And here, that position is taken up by a receive (r start), which is coming from the send (s start) on the main patch. So we've learned a little more about sub-patches. You can get data in or out via outlet (and, conversely, inlet) or by sends.

Sends, in pure data, actually work between any open pure data patch, as long as they have the same name.

So when we click the toggle in the top right corner, the rhythm generation is done inside our sub-patch, then bangs are passed out of it to click~ and then to our sound card via dac~. It's easy enough to follow through, all we've done is put it in a box, labelled with rhythm, so we know what it does to give us space for the rest of our monster patch.

You can close the window containing the sub-patch and the work it does is still there. Clicking on the sub-patch (pd) object when you're not in edit mode will open the sub-patch window again.

I've also thought ahead a little, here. The patch from Sequencing and Wavetables, Part 1: Arrays in Pure Data starts with a metro, meaning that it's actions are started by bangs. So I've ended my rhythm generation sub-patch just after it's own metro, and put the sound-generation part of it in the main patch. This makes it easier to swap it out for something else that accepts bangs...

Step 3: Back to a sequence generator...

And the content of the sub-patch, sequence:

Now, we've used inlet, to allow us to connect the output of pd rhythm to the pd sequence. Then filled the sub-patch sequence with the middle part of the step-sequencer from Sequencing and Wavetables, Part 1: Arrays in Pure Data. It's the part after the metro (rhythm works a bit like metro, only it's outputting bangs in a more complex sequence) and before it starts producing sound (be cause we know we're going to use it a little later to feed the AM/FM synthesizer from Algorithms of the Street: Part 2 - Amplitude Modulation).

First things first, we've got an object here called loadbang. It's quite simple what this does, it outputs a bang when your file is loaded. In this case, that bang sets the values of the two arrays, scale (which is hidden in the table object, within the sequence sub-patch) and sequence (which is out on the parent patch).

It may seem unusual that I've put the sequence table on the parent, but hidden the numbers setting it away in the sub-patch with the code that's reading the table. But there's a pattern to what we're doing here. I've put things we might want to change, or play with, on the parent, where we can see them, and hidden away the things we might want to change less often, like the code that's playing the sequence and the list of numbers we're going to start with each time.

If everything's working, when you check the toggle in the top right corner, you should see the numbers below pd sequence changing. Now, we can use this to create a melody with our old friend, the oscillator...

This sounds fairly pleasant, but this is an exercise in 'bringing it all together' and I've already written some blog posts about synthesis algorithms, so let's put that into practice...

Step 4: Give the oscillator the boot...

And the contents of the synthesiser sub-patch:

Now, as I explained in Algorithms of the Street, Part 1: Frequency Modulation, anywhere you see an oscillator you can replace it with a more complex synthesis algorithm. The only problem is that this takes up more space on screen (also more processing power, but at this stage it's not likely to be enough to effect your computers performance, so it's not a huge concern). So here I've set up our third sub-patch to respond to numbers in the same way the osc~ object does, accepting a number, and outputting a tone at that frequency.

Also note that instead of using outlet, we're using outlet~. Again, a common pattern in Pure Data, this means it's an audio outlet, as opposed to a signal one. So we're outputting a sound.

With the added bonus that we're controlling the tone from a grid, again on the parent patch, because we may want to change it. And we've achieved what we initially set out to do. The rhythm generation, sequencer and synthesizer hidden away in neat little boxes, and I've done it in just 4 steps. So let's see if we can push this a little further.

Step 5: Solve a problem with a hidden volume control

Try turning off the toggle in the top right corner (start). You'll notice that the sound doesn't stop, it just stops changing it's pitch, and stays maddeningly un-changing. So let's fix this issue with an extra sub-patch...

And the contents of pd volume:

All we've done here is hide our volume controls in another sub-patch, then added a volume envelope which hijacks the start signal, and uses it to control the volume (the 1 and 0 used by toggles multiplies the sound by 1 or 0, leaving it unchanged or silent) with a line generated taking 1 second to fade in and out.

We've fixed an inherent problem with our code without filling up a lot of space on screen with the extra few objects that simply fade in and out.

Another problem with our patch is that the notes are all produced at an identical volume, whereas real sounds, in the real world tend to have what we call a volume envelope, meaning that the volume of the sound changes over time. Think, if you pluck a string, the first part of the sound is loud, then it fades out over time. So let's fix this in another sub-patch...

Step 6: A volume envelope for each note...

And the contents of pd volume-envelope:

The vline~ object is a lot like line~, used in How to make a Musical Instrument in Ten Minutes. Only its messages are a little more advanced. It has groups of arguments, separated by commas. So each one has:

  • Go to this value
  • In this number of milliseconds
  • After waiting this number of milliseconds
Meaning this takes 50 milliseconds to get up to 1, goes down to 0.4 in 50 milliseconds (when the first one is finished) then fades down to 0 in 200 milliseconds when the second is finished. This is a very simple volume envelope but it gives each note a definite start (attack) and end (release). The actual volume envelope, plotted in an array, looks like this:

And because we can write it into an array in Pure Data, we can also read it out of one to perform the same purpose. But more on that in a later post. 

Again, piece by piece we're making this patch more advanced, but not taking up much room, putting things in little boxed labelled with their purpose. Meaning if we want to change something about one or other part of our patch, we can find it fairly easily.

We also know, having put it together, what we expect to go in to each sub-patch, and what we expect to come out of each one. So if we want to debug them, we can just give one sub-patch the input it expects and see if the output is what we expect.

So, for instance, synthesizer expects a number, so we could feed it a number from a number box, and it outputs a tone, so we could just put a dac~ after it and see if it changes when you change the number. That is actually a common feature of programming computers, what we call layers. Different parts of an application which are built on one another, but only rely on the previous layer. So we can test each one individually.

Also, a quick note for the conscientious on sub-patches. Their strength is that they hide code away, but it's also their weakness. If you don't know what a sub-patch does, you won't be able to fix it very easily. Also, in a patch like this you have to open each sub-patch individually to find out exactly what it's doing, which might take quite a lot of time to fully understand if you aren't the one who put it together.

There's a balance to be struck, here. For instance, replacing osc~ with a sub-patch is a fairly common pattern, as it does exactly what osc~ does, but differently. What we tend to call a contract in programming terms. The contract of an osc~ is that it takes a frequency (in herz) and outputs a waveform. Our synthesizer sub-patch does just that, so it's easy to directly replace.

However, to make the patch more readable, almost everything else in it could be on show. Although exactly how much Pure Data you hide in a box is, ultimately, a personal decision. What makes sense to you?

Right, now that the discussion of concept is out of the way, let's plug in one last component. The delay line from How to make a Musical Instrument in Ten Minutes

Step 7: Just for fun, delay.

And the contents of pd delay:

This is just two extra UI elements (a vertical slider, and a vertical radio) feeding in to the classic delay line as used in How to make a Musical Instrument in Ten Minutes (go there if you want to know what this part of the patch is actually doing). And you have control of a part-generative, part sequenced synthesizer and a delay effect.

Have fun messing around with electronic sound...

Well, that's it. Feel free to get in touch via @MarmiteJunction and let me know if you managed to do this (it's about as advanced a Pure Data patch as I've blogged so far) and what you'd like to see explained here.

Until next time, God bless and happy coding.

No comments:

Post a Comment