Despite all of this, I’m still relatively new at Pure Data and the Max language. To those who chime in with corrections or clarifications in the comments, you are most appreciated! If you’re new to PD, make sure you check the comments section for clarifying info provided by generous souls.
In the first article of this synthesizer construction series, we built a patch that imports a sound file to PureData and plays it back. This time, we’re going to be working on an “under the hood” function for our patch that will be critical in constructing the wavetable sample out of a portion of the imported sound file. In order to control the length of these articles, I’m going to operate under the assumption that you’ve read and completed the previous installments.
That picture above is what we’re going to be left with when we finish today’s task. Don’t let the addition of only four “atoms” fool you, this one’s going to take a while. In order to help us keep things cleaner, better organized, and easier to trace…we’re going to introduce sub-patches and a more extensive use of variables to our patch. We’ll also be looking at the idea of “hot” and “cold” inlets on atoms; as well as how they affect the order of operations in PD.
Let’s start out by opening your patch from the previous project, and saving it with a new name…just in case. The method of playback that will be used in our final synthesizer has a very specific form of optimization. It wants to see arrays of a particular size…[an exponential value of 2] + 3. If we were to start listing the potential values that results in, we’d have the following set: 7 ([2 squared)+3), 11 ([2 cubed]+3), 19, 35, 67…and so on. We also don’t want our wavetable sample to be larger than our actual sound file. Now, we could calculate a much larger set of results than what I listed above and compare the file size to those fixed values, but I’d rather build a computational process that can quickly to that for me.
First, we need to know what the total length of our sound file is. This is fairly easy for us to determine. The outlet of [soundfiler] will be the total number of values pulled from the sound file. Add a “number” atom to your patch (“Put” or cmd+3) and connect it to [soundfiler]. Import a sound file, and that number atom will display a value. If you get a “+” instead of a number, that simply means that the value has too many digits to display in the atom. You can change the “width” parameter in the right-click->properties menu of the number atom. So, we now know how to get our file size.We’re going to assign that value to a global variable. Disconnect that number atom by clicking on the connection line (it should highlight blue) and then pressing delete. Set it to the side, and add a new object atom to the patch (cmd+1). In that atom, type “send $0-filelength.” Then connect it to [soundfiler]. The [send] atom is a “wireless” connection that can be used within a patch. It pairs with the [receive] atom, which we’ll be getting to a bit later. I mention it here, because your PD console may have listed an error to the effect that there is no receiving object. Things are fine for now. Also, Many object atoms require “arguments” to define exactly where their supposed to send data and perform functions, define what other components of your patch they should use to perform their function, or to simply tell them HOW to perform their function. In these cases, we have to uses spaces to separate the command from the argument. The [send] / [receive] pair is a good example of this. The “$0-filelength” is the variable to which we are assigning the value from [soundfiler]. The dollar sign ($) plus a number always indicates a variable. You can add a “-” and a text string after a variable (no spaces!) to give the variable a unique name…something that identifies what the variable is carrying. We’re using the [send] atom, because we’ll eventually be accessing this file size value at multiple locations in the patch. It prevents us from having a bunch of connection lines cluttering up the visual space.
For the heck of it, let’s use that [send] to feed the number atom we set aside a short while ago. Right-click the number atom and select “properties.” If you haven’t already, change the width to 10, then look for the field labeled “receive symbol.” Click in that field and type in our new variable, “$0-filelength”. Import a new file, and watch that atom fill itself with a number. We’re going to be using this method of throwing data around the patch without any VISUAL connections quite a bit.Slide that number atom down and a bit out of the way, then add a new object atom. In this atom, we’re going to type “pd power”. When you’ve finalized that atom, a new window will open. We’ve just created a sub-patch.We’re going to use this sub-patch to run a bunch of code that we don’t need to see in the main patch, and we’ll be working exclusively in it until the end of this article. The nice thing about that global variable we created a moment ago is that sub-patches have access to them too. Because we’re going to be using this sub-patch for a computational process based on the sound file we import, we only want it to run when we’ve imported a file…otherwise we’re just wasting processor power. Let’s control this with a [bang], and we’ll use the “button” style bang again here (shift+cmd+b). Place that in your sub-patch and open its “properties” window. Look for the receive field and enter “$0-filelength” once again. This will start the computation as soon as file size is known.We’re going to need a little bit more control than just that. We’ll need a way to turn the computation process off once it’s done. For that we’re going to use an atom called “toggle”. [toggle] is another visual atom, much like the button [bang]. It has two states, checked and unchecked, and outputs a value of 1 or 0 in those respective states. So, a ‘1’ from the toggle will start computation, while a ‘0’ will stop it. We’ll use the [bang] to turn the [toggle] on. Connect the [bang] to the [toggle].What we need to compute now, is the result of [2 to an exponential power]+3. We need to know at what exponential power this equation’s result will exceed our file size, and use the value just prior to that as our maximum wavetable sample size. So, we need a way to count the exponent up. The first part of our “counting machine” will be the [metro] atom. [metro] is just like a metronome; it outputs a bang once every ‘n’ milliseconds. It can be created with an initial value, or be fed that value externally. We’re going to create one with an initial value of “1”. Create an object atom and type in “metro 1”. This [metro 1] atom we’ve just created will output a “bang” every millisecond while it is active. The left inlet controls [metro]’s on/off state. When it receives a “1”, [metro] is on. It is off, when it receives a “0”. The [bang] we initially placed only outputs a one when it receives a message, meaning it would not keep [metro] on. This is why we placed the [toggle] after the [bang]; it changes STATE whenever it receives a message or bang. The right inlet on [metro] receives an argument that replaces any initial arguments (i.e. our ‘1’). That can be used to change the pace, or rate of bangs, output by [metro]’s outlet…a higher number means a slower rate. We’re setting up this “counting machine” to be quick. Now that we’re done talking about the [metro] atom, go ahead and connect its left inlet to the [toggle].The second half of our counting process will require two object atoms. Create those and type “float 1” into one, and “+ 1” into the other (note the space before the arguments in each). We’re creating a [float] atom, which outputs a number when it receives a message or bang, and a math atom, which does basic math operations. This is where we start talking about “hot” and “cold” inlets. PureData does most things from right to left. In general, you want to feed the “cold” inlet of an atom before you feed its “hot” inlet. The data/messages/bangs at an atom’s hot inlet tells it to preform its function. You can change values at the cold inlet all you want, but the atom won’t output until it receives something from its hot. This means that it’s easy to screw things up, and get invalid results, if you’re not paying attention. The “hot” inlet is always the furthest to the left. To get you used to that, I will be referring to the left inlet as the “hot” inlet from now on.
[float] will output its initial argument whenever it receives a bang…unless that argument is overwritten by a value at it’s right inlet. We’re going to use the [float 1] and the [+ 1] together to do our counting. Connect the [float 1] outlet to the inlet of [+ 1]. Now connect the outlet of [+ 1] to the cold (or right) inlet of [float 1]. Connect [metro] to the hot inlet of [float 1].
Here’s what’s going to happen when this activates now. The [bang] will set the [toggle] on, which will activate [metro 1]. [metro 1] will remain active, sending a bang out once every millisecond. The first time [float 1] receives a bang from [metro 1] it will output “1.” This “1” will pass to [+ 1], which will output “2.” That will feed to the cold inlet of [float 1], replacing “1” in memory (the “1” will remain as the argument in your [float] atom however). On the next bang 2 will be output, and 3 will be stored. So, the output of [float 1] is incrementally counting by 1, each time it receives a bang. We are now counting. If you’d like to test it, connect a number atom to the outlet of [float 1], switch out of edit mode (cmd+e) and click on the [toggle] (it will fill with an “X”). The value in the number atom should be rapidly increasing. Click the [toggle] once more to stop the counting.Disconnect that number atom and slide it out of the way. We’ll make use of it again later. The next thing we need to do is use those numbers produced by float to generate the result of [2 to the ‘n’ power]. Create a new object atom and enter “pow”. The [pow] atom performs an exponent calculation. It can have an initial value, or be fed a value through its cold inlet. The base, “2” for our needs, will be fed to the hot inlet. [We’ll come back to that.] Now, we can’t just feed the output of [float 1] to the cold inlet of [pow], because that will not actually trigger it to output a result. What we need to do, is send the output of [float 1] to the cold inlet, then send the value “2” to the hot inlet to make it output a result. We’re going to control this with the [trigger] atom.
[trigger] can be used to control a sequence of events, and it can be used for as few as 1 or as many as 100…or more. It is perfectly predictable in that the first event it outputs is the one furthest to the right in its argument range; the last output is the furthest to the left. We’ve already discussed that we need to control a sequence of two events. Create an object atom and type “trigger bang float” or “t b f” into it (the second is a set of abbreviations that PD recognizes…it means exactly the same thing). We will connect the output of [float 1] to [t b f]. The right outlet of [t b f] will output the “float” value it receives at it’s inlet; connect that to the cold inlet of [pow]. Now we have our exponent routing to [pow]. Next we need to feed the hot inlet of [pow] to generate a result. Place a message box (cmd+2) into the sub-patch, and type “2” into it. Remember that a message atom outputs its contents whenever it receives a bang. Connect the left outlet of [t b f] (which is a bang), to the new message atom. Connect the message atom to the hot inlet of [pow]. There we have it. We are now feeding [pow] the values it needs in the correct order, and it will trigger the calculation every time a new value is sent from [float 1]. To finish up our [2 to the power of ‘n’]+3 equation, let’s add one more math atom to the sub-patch. Place an object with the contents “+ 3” (remember the space for the argument) and connect it to the outlet of [pow].Our next step is to compare the results of [pow] to the length of our imported file. PD can do all of your standard boolean comparisons as a math atom, but we’ll take this opportunity to introduce another interesting atom that we’ll be coming back to frequently in this series. We’re going to use the object atom, [moses]; go ahead and place one in your sub-patch. [moses] compares a value at its hot inlet to an initial argument (or a value at its cold inlet). If the hot inlet is greater than or equal to the value, it outputs that value at its right outlet; otherwise, the value is output at its left outlet. Let’s use that number atom we set aside a short while ago to feed the cold inlet of [moses]. Right-click the number atom and select “properties.” In the “receive symbol” field, enter “$0-filelength”. Now, [moses] will compare the results of [pow] and [+ 3] to the length of the imported file. At the moment, we’re only concerned with when the results are greater than the file length. So, we’re going to ignore [moses]’s left outlet.
When the result does exceed the file length, we want this sub-patch to stop calculations. We’re going to do this through the use of a message atom and
a “variable object” (probably not correct, but how I’ll refer to it) an alternative form of “send”. [Thanks to Kristof Lauwers for the correction. See his comment below for more excellent information on “sends” and variables.] Place a message atom and do the following; type “;”, press enter/return and type “stopcalc 0”. The “;” tells PD that this message atom is an internal command, and that it should set the value of “stopcalc” to “0”. Note that when defined in this manner, this variable does not require the usual “$” plus a number. I don’t know if that’s the correct name for it, but it should work for the purposes of this tutorial. You can use this “internal command” method to execute multiple commands at once too. You simply need a “;” after each command, excluding the final one (which is why “stopcalc 0” doesn’t have one).We’re now going to go back to the top of this sub-patch and add a [receive] atom. We first talked about this object back when I introduced its partner the [send] atom. Your atom should look like this: [receive stopcalc]. Connect that atom to the inlet of [toggle]. Toggle is now being fed by both [bang] and [receive stopcalc]; [bang] starts the calculation, and [receive stopcalc] feeds the [toggle] a zero value…turning it (and the calculation process) off.So how do we now output the “maximum exponent value” that we need for the rest of the synthesizer patch? When the calculation stops, [float 1] is now holding, in memory, the exponent value that is one integer too high for use with our sound file. We only need to subtract “1” and we’ll have our number. Add an object and make it a [- 1] math atom. Connect that to the output of [float 1]. Then create one more object and type in “outlet”. If you have your “parent” patch visible as well, you’ll notice that your [pd power] atom has just gained an outlet box. [note: You create inlets for sub-patches the same way…[inlet]. We’ll be doing that in the future as well.] Connect [- 1] to your new [outlet] atom. This sub-patch will now output a new exponent every time [float 1] receives a bang. When the calculation process of the sub-patch is stopped by a “0” value from [receive stopcalc], the sub-patch will have output the largest exponent that can be used without exceeding the imported file’s length.We have one more step that is necessary before we are finished with this sub-patch. In case we decide to import a new file, we need a way to reset [float 1]. [float 1] will be holding its last value in memory until it is reloaded. So, if the value that was one too high is 13, that’s currently where its next set of calculations will start…13. That’s not very helpful if the file is shorter, because it won’t work at all then! Create a message atom with the value “1”. Connect that atom to the outlet of [receive stopcalc] and to the cold inlet of [float 1]. Whenever “stopcalc” is redefined as 0, it will bang the  message atom. The value held in memory for [float 1] will be replaced with a “1”, leaving the sub-patch ready to do its thing the next time a file is loaded.Now we can close the sub-patch and return to the “parent” patch. In preparation of what we’ll be doing next time, let’s add one last atom to this patch. Add the object atom [s $0-maxpower]. You should recognize what is the variable in this atom by now, and the “s” is merely an abbreviation for “send” (much like we just used in the sub-patch with [t b f]). Connect that atom to the outlet of [pd power] and we’ve reached as far as we’re going today.Not nearly as exciting as the first step in this series, but we’ve laid some important groundwork for where we’re headed next. In the next article, we’ll be creating the means to define all of the parameters we need to select our wavetable sample out of the imported file. See you then.