From 5183815695803de720994aa86af9d5348403373b Mon Sep 17 00:00:00 2001 From: Albert Graef Date: Thu, 5 Sep 2024 23:06:05 +0200 Subject: [PATCH] Update tutorial --- tutorial/pd-lua-intro.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tutorial/pd-lua-intro.html b/tutorial/pd-lua-intro.html index 527fd2c..cfff897 100644 --- a/tutorial/pd-lua-intro.html +++ b/tutorial/pd-lua-intro.html @@ -704,7 +704,7 @@ pd-lua-intro
-

A Quick Introduction to Pd-Lua

Albert Gräf <aggraef@gmail.com>
Computer Music Dept., Institute of Art History and Musicology
Johannes Gutenberg University (JGU) Mainz, Germany
September 2024

This document is licensed under CC BY-SA 4.0. Other formats: Markdown source, PDF
Permanent link: https://agraef.github.io/pd-lua/tutorial/pd-lua-intro.html

Why Pd-Lua?

Pd's facilities for data structures, iteration, and recursion are somewhat limited, thus sooner or later you'll probably run into a problem that can't be easily solved by a Pd abstraction any more. At this point you'll have to consider writing an external object (or just external, for short) in a "real" programming language instead. Pd externals are usually programmed using C, the same programming language that Pd itself is written in. But novices may find C difficult to learn, and the arcana of Pd's C interface may also be hard to master.

Enter Pd-Lua, the Pd programmer's secret weapon, which lets you develop your externals in the Lua scripting language. Pd-Lua was originally written by Claude Heiland-Allen and has since been maintained by a number of other people in the Pd community. Lua, from PUC Rio, is open-source (under the MIT license), mature, very popular, and supported by a large developer community. It is a small programming language, but very capable, and is generally considered to be relatively easy to learn. For programming Pd externals, you'll also need to learn a few bits and pieces which let you interface your Lua functions to Pd, as explained in this tutorial, but programming externals in Lua is still quite easy and a lot of fun. Using Pd-Lua, you can program your own externals ranging from little helper objects to full-blown synthesizers, sequencers, and algorithmic composition tools. It gives you access to Pd arrays and tables, as well as a number of other useful facilities such as clocks and receivers, which we'll explain in some detail. Pd-Lua also ships with a large collection of instructive examples which you'll find helpful when exploring its possibilities.

Pd-Lua was originally designed for control processing, so we used to recommend Faust for doing dsp instead. We still do, but Faust isn't for everyone; being a purely functional language, Faust follows a paradigm which most programmers aren't very familiar with. Fortunately, thanks to the work of Timothy Schoen, the most recent Pd-Lua versions now also provide support for signal processing and even graphics. So it is now possible to create pretty much any kind of Pd object in Lua, including dsp objects. (However, Faust will almost certainly execute dsp code much more efficiently than Pd-Lua, as it generates highly optimized native code for just this purpose.)

Note that we can't possibly cover Pd or the Lua language themselves here, so you'll have to refer to other online resources to learn about those. In particular, check out the Lua website, which has extensive documentation available, and maybe have a look at Derek Banas' video tutorial for a quick overview of Lua. For Pd, we recommend the Pd FLOSS Manual at https://flossmanuals.net/ to get started.

Installation

Pd-Lua works inside any reasonably modern Pd flavor. This encompasses vanilla Pd, of course, but also Purr Data which includes an up-to-date version of Pd-Lua for Lua 5.4 and has it enabled by default, so you should be ready to go immediately; no need to install anything else. The same is true for plugdata (version 0.6.3 or later), a Pd flavor which can also run as a plug-in inside a DAW.

With vanilla Pd, you can install the pdlua package from Deken. There's also an official Debian package, maintained by IOhannes Zmölnig. You can also compile Pd-Lua from source, using the author's Github repository. Compilation instructions are in the README, and you'll also find some Mac and Windows binaries there. In either case, after installing Pd-Lua you also have to add pdlua to Pd's startup libraries.

If all is well, you should see a message like the following in the Pd console (note that for vanilla Pd you'll have to switch the log level to 2 or more to see that message):

This will also tell you the Lua version that Pd-Lua is using, so that you can install a matching version of the stand-alone Lua interpreter if needed. Lua should be readily available from your package repositories on Linux, and for Mac and Windows you can find binaries on the Lua website. In the following we generally assume that you're using Lua 5.3 or later (using Lua versions older than 5.3 is not recommended).

If all is not well and you do not see that message, then most likely Pd-Lua refused to load because the Lua library is missing. This shouldn't happen if you installed Pd-Lua from a binary package, but if it does then you may have to manually install the right version of the Lua library to make Pd-Lua work. Make sure that you install the package with the Lua library in it; on Debian, Ubuntu and their derivatives this will be something like liblua5.4-0.

A basic example

With that out of the way, let's have a look at the most essential parts of a Lua external. To make an external, say foo, loadable by Pd-Lua, you need to put it into a Lua script, which is simply a text file with the right name (which must be the same as the object name, foo in this case) and extension (which needs to be .pd_lua), so the file name will be foo.pd_lua in this example.

Any implementation of an object must always include:

  • a call to the pd.Class:new():register method which registers the object class with Pd (this should always be the first line of the script, other than comments)

  • a definition of the initialize method for your object class

Here is a prototypical example (this is the contents of the foo.pd_lua file):

And here's the same patch again, which now lets us drag the hand to change the phase value:

18-graphics3

More dial action: clocks and speedometers

Now that our dial object is basically finished, let's do something interesting with it. The most obvious thing is to just turn it into a clock (albeit one with just a seconds hand) counting off the seconds. For that we just need to add a metro object which increments the phase angle and sends the value to the dial each second:

18-graphics4

Pd lets us store the phase angle in a named variable (v phase) which can be recalled in an expr object doing the necessary calculations. The expr object sends the computed value to the phase receiver, which updates both the variable and the upper numbox, and the numbox then updates the dial. Note that we also set the variable whenever the dial outputs a new value, so you can also drag around the hand to determine the starting phase. And we added a 0 message to reset the hand to the 12 o'clock home position when needed.

Here's another little example, rather useless but fun, simulating a speedometer which just randomly moves the needle left and right:

18-graphics5

I'm sure you can imagine many more creative uses for this simple but surprisingly versatile little GUI object, which we did in just a few lines of fairly simple Lua code. Have fun with it! An extended version of this object, which covers some more features of the graphics API that we didn't discuss here to keep things simple, can be found as dial.pd and dial.pd_lua in the tutorial examples:

18-graphics6

The extended example adds messages for resizing the object and setting colors, and also shows how to save and restore object state in the creation arguments using the set_args() method mentioned at the beginning of this section. The accompanying patch covers all the examples we discussed here, and adds a third example showing how to utilize our dial object as a dB meter.

Live coding

I've been telling you all along that in order to make Pd-Lua pick up changes you made to your .pd_lua files, you have to relaunch Pd and reload your patches. Well, as you probably guessed by now, this isn't actually true. So in this section we are going to cover Pd-Lua's live coding features, which let you modify your sources and have Pd-Lua reload them on the fly, without ever exiting the Pd environment. This rapid incremental style of development is one of the hallmark features of dynamic interactive programming environments like Pd and Lua. Musicians also like to employ it to modify their programs live on stage, which is where the term "live coding" comes from.

I've kept this topic for the final section of this guide, because it is somewhat advanced, and there are several different (traditional and new) methods available which differ in capabilities and ease of use. However, in modern Pd-Lua versions there is one preferred method which rules them all, so if you want to cut to the chase as quickly as possible, then feel free to just skip ahead to the pdx.lua section now.

If you're still here, then you probably want to learn about the other methods, too. So in the following we first describe the predefined Pd-Lua object classes pdlua and pdluax, so that you know the "traditional" live-coding instruments that Pd-Lua had on offer for a long time. We also discuss how to add a reload message to your existing object definitions. This is quite easy to do by directly employing Pd-Lua's dofile function, which is also what both pdlua and pdluax use internally.

These methods all work with pretty much any Pd-Lua version out there, but require a little bit of setup which can get time-consuming and error-prone if you're dealing with large patches involving a lot of different Lua objects.

That's why Pd-Lua nowadays includes an extension module called pdx.lua that "just works" out of the box and automatizes everything, so that you only have to put a simple reload message in your main patch and be done with it. This is also the method we recommend, to novices and expert users alike. We describe it last so that you can also gather a good understanding of Pd-Lua's traditional live coding methods, and learn to appreciate them. These are still used in many older scripts, and Pd-Lua will continue to support them for backward compatibility.

pdlua

The pdlua object accepts a single kind of message of the form load filename on its single inlet, which causes the given Lua file to be loaded and executed. Since pdlua has no outlets, its uses are rather limited. However, it does enable you to load global Lua definitions and execute an arbitrary number of statements, e.g., to post some text to the console or transmit messages to Pd receivers using the corresponding Pd-Lua functions. For instance, here's a little Lua script loadtest.lua which simply increments a global counter variable (first initializing it to zero if the variable doesn't exist yet) and posts its current value in the console:

To run this Lua code in Pd, you just need to connect the message load loadtest.lua to pdlua's inlet (note that you really need to specify the full filename here, there's no default suffix):

pdlua example

Now, each time you click on the load loadtest.lua message, the file is reloaded and executed, resulting in some output in the console, e.g.:

Also, you can edit the script between invocations and the new code will be loaded and used immediately. E.g., if you change counter + 1 to counter - 1, you'll get:

That's about all there is to say about pdlua; it's a very simple object.

pdluax

pdluax is a bit more elaborate and allows you to create real Pd-Lua objects with an arbitrary number of inlets, outlets, and methods. To these ends, it takes a single creation argument, the basename of a .pd_luax file. This file is a Lua script returning a function to be executed in pdluax's own initialize method, which contains all the usual definitions, including the object's method definitions, in its body. This function receives the object's self as well as all the extra arguments pdluax was invoked with, and should return true if creation of the object succeeded.

For instance, here's a simplified version of our foo counter object, rewritten as a .pd_luax file, to be named foo.pd_luax:

Note the colon syntax self:in_1_bang(). This adds the bang method directly to the self object rather than its class, which is pdluax. (We obviously don't want to modify the class itself, which may be used to create any number of different kinds of objects, each with their own collection of methods.) Also note that the outer function is "anonymous" (nameless) here; you can name it, but there's usually no need to do that, because this function will be executed just once, when the corresponding pdluax object is created. Another interesting point to mention here is that this approach of including all the object's method definitions in its initialization method works with regular .pd_lua objects, too; try it!

In the patch, we invoke a .pd_luax object by specifying the basename of its script file as pdluax's first argument, adding any additional creation arguments that the object itself might need:

pdluax example

These pdluax foo objects work just the same as their regular foo counterparts, but there's an important difference: The code in foo.pd_luax is loaded every time you create a new pdluax foo object. Thus you can easily modify that file and just add a new pdluax foo object to have it run the latest version of your code. For instance, in foo.pd_luax take the line that reads:

Now change that + operator to -:

Don't forget to save your edits, then go back to the patch and recreate the pdluax foo object on the left. The quickest way to do that is to just delete the object, then use Pd's "Undo" operation, Ctrl+Z. Et voilà: the new object now decrements the counter rather than incrementing it. Also note that the other object on the right still runs the old code which increments the counter; thus you will have to give that object the same treatment if you want to update it, too.

While pdluax was considered Pd-Lua's main workhorse for live coding in the past, it has its quirks. Most notably, the syntax is different from regular object definitions, so you have to change the code if you want to turn it into a .pd_lua file. Also, having to recreate an object to reload the script file is quite disruptive (it resets the internal state of the object), and may leave objects in an inconsistent state (different objects may use various different versions of the same script file). Sometimes this may be what you want, but it makes pdluax somewhat difficult to use. It's not really tailored for interactive development, but it shines if you need a specialized tool for changing your objects on a whim in a live situation.

Fortunately, if you're not content with Pd-Lua's traditional facilities for live coding, it's easy to roll your own using the internal dofile method, which is discussed in the next subsection.

dofile and dofilex

So let's discuss how to use dofile in a direct fashion. Actually, we're going to use its companion dofilex here, which works the same as dofile, but loads Lua code relative to the "externdir" of the class (the directory of the .pd_lua file) rather than the directory of the Pd canvas with the pdlua object, which is what dofile does. Normally, this won't make much of a difference, but it will matter if Lua class names are specified using relative pathnames, such as ../foo or bar/baz. Since we're reloading class definitions here, it's better to use dofilex so that our objects don't break if we move things about.

The method we sketch out below is really simple and doesn't have any of the drawbacks of the pdluax object, but you still have to add a small amount of boilerplate code to your existing object definition. Here is how dofilex is invoked:

The return values of dofilex are those of the Lua script, along with the path under which the script was found. If the script itself returns no value, then only the path will be returned. (We don't use any of this information in what follows, but it may be useful in more elaborate schemes. For instance, pdluax uses the returned function to initialize the object, and the path in setting up the object's actual script name.)

Of course, dofilex needs the name of the script file to be loaded. We could hardcode this as a string literal, but it's easier to just ask the object itself for this information. Each Pd-Lua object has a number of private member variables, among them _name (which is the name under which the object class was registered) and _scriptname (which is the name of the corresponding script file, usually this is just _name with the .pd_lua extension tacked onto it). The latter is what we need. Pd-Lua also offers a whoami() method for this purpose, but that method just returns _scriptname if it is set, or _name otherwise. Regular Pd-Lua objects always have _scriptname set, so it will do for our purposes.

Finally, we need to decide how to invoke dofilex in our object. The easiest way to do this is to just add a message handler (i.e., an inlet method) to the object. For instance, say that the object is named foo which is defined in the foo.pd_lua script. Then all you have to do is add something like the following definition to the script:

As we already discussed, this code uses the object's internal _scriptname variable, and so is completely generic. You can just copy this over to any .pd_lua file, if you replace the foo prefix with whatever the name of your actual class variable is. With that definition added, you can now just send the object a reload message whenever you want to have its script file reloaded.


NOTE: This works because the pd.Class:new():register("foo") call of the object only registers a new class if that object class doesn't exist yet; otherwise it just returns the existing class.

By reloading the script file, all of the object's method definitions will be overwritten, not only for the object receiving the reload message, but for all objects of the same class, so it's sufficient to send the message to any (rather than every) object of the class. Also, existing object state (as embodied by the internal member variables of each object) will be preserved.

In general all this works pretty well, but there are some caveats, too. Note that if you delete one of the object's methods, or change its name, the old method will still hang around in the runtime class definition until you relaunch Pd. That's because reloading the script does not erase any old method definitions, it merely replaces existing and adds new ones.

Finally, keep in mind that reloading the script file does not re-execute the initialize method. This method is only invoked when an object is instantiated. Thus, in particular, reloading the file won't change the number of inlets and outlets of an existing object. Newly created objects will pick up the changes in initialize, though, and have the proper number of inlets and outlets if those member variables were changed.


Let's give this a try, using luatab.pd_lua from the "Using arrays and tables" section as an example. In fact, that's a perfect showcase for live coding, since we want to be able to change the definition of the waveform function f in luatab:in_1_float on the fly. Just add the following code to the end of luatab.pd_lua:

Now launch the luatab.pd patch and connect a reload message to the luatab wave object, like so:

Livecoding example 1

Next change the wavetable function to whatever you want, e.g.:

Return to the patch, click the reload message, and finally reenter the frequency value, so that the waveform gets updated:

Livecoding example 2

pdx.lua

The method sketched out in the preceding subsection works well enough for simple patches. However, having to manually wire up the reload message to one object of each class that you're editing is still quite cumbersome. In a big patch, which is being changed all the time, this quickly becomes unwieldy. Wouldn't it be nice if we could equip each object with a special receiver, so that we can just click a message somewhere in the patch to reload a given class, or even all Pd-Lua objects at once? Or even send that message via the pdsend program, e.g., from the text editor in which you edit the Lua source of your object?

Well, all this is in fact possible, but the implementation is a bit too involved to fully present here. So we have provided this in a separate pdx.lua module, which you can find in the sources accompanying this tutorial for your perusal. As of Pd-Lua 0.12.8, pdx.lua is pre-loaded and all the required setup is performed automatically. You only have to add a message like the following to your patch, which goes to the special pdluax receiver (note that this is unrelated to the pdluax object discussed previously, it just incidentally uses the same name):

When clicked, this just reloads all Pd-Lua objects in the patch. You can also specify the class to be reloaded (the receiver matches this against each object's class name):

Or maybe name several classes, like so:

You get the idea. Getting set up for remote control via pdsend isn't much harder. E.g., let's say that we use UDP port 4711 on localhost for communicating with Pd, then you just need to connect netreceive 4711 1 to the pdluax receiver in a suitable way. Let's use the luatab.pd_lua object from the previous subsection as an example. You can remove the in_1_reload handler from that script -- it's no longer needed, as pdx.lua now dispatches the reload messages for us. Here's how the revised patch looks like:

Remote control

This doesn't look any simpler than before, but it also does a whole lot more. Clicking the message not just reloads the luatab script, but any Lua object you have running, in any of the patches you have opened. And you can now use pdsend 4711 localhost udp to reload your Lua objects from literally anywhere. Any decent code editor will let you bind a keyboard command which does this for you. Myself, I'm a die-hard Emacs fan, so I've included a little elisp module pd-remote.el which shows how to do this. Once you've added this to your .emacs, you can just type Ctrl+C Ctrl+K in any Lua buffer to make Pd reload your Lua scripts after editing them. It doesn't get much easier than that.

In addition, I've provided a little abstraction named pd-remote.pd which takes care of adding the netreceive and messaging bits and also looks much tidier in your patches. Using the abstraction is easy: Insert pd-remote into the patch you're working on, and (optionally) connect a pdluax reload message (without the ; prefix) to the inlet of the abstraction; or use something like pdluax reload foo to reload a specific object class. Now you can just click on that message to reload your script files, and the abstraction will also respond to such messages on port 4711 (the port number can be changed in the abstraction if needed). Here's how that looks like in a patch:

Remote control


NOTE: To make Pd find the pd-remote.pd abstraction without having to copy it to your project directory, you can add the pdlua external directory (which is where the abstraction gets installed) to your Pd library path, either in your Pd setup, or inside the patch with a declare -stdpath object, as shown above.

The pd-remote.el file can be installed in your Emacs site-lisp directory if needed. However, the easiest way to install it is from MELPA, a large repository of Emacs packages. Please also check the pd-remote repository on GitHub for the latest pd-remote version and further details. This also includes a pointer to a Visual Studio Code extension written by Baris Altun which can be used as a replacement for pd-remote.el if you prefer VS Code for editing.


And here's a little gif showing the above patch in action. You may want to watch this in Typora or your favorite web browser to make the animation work.

Remote control

So there you have it: Not one, not two, but three different ways to live-code with Pd-Lua (or four, if you count in the pdlua object). pdx.lua certainly is the most advanced and user-friendly solution among these, but you can choose whatever best fits your purpose and is the most convenient for you.

Object reinitialization in pdx.lua

If pdx.lua reloads a script file, it normally does not run the initialize method. This is by design, as we want the reload process to be as smooth as possible while running a patch, and invoking the initialize method could potentially be quite disruptive, as it usually reinitializes all your member variables.

However, pdx.lua has another trick up its sleeve if you do need some kind of custom reinitialization. There are two callback methods that you can implement, prereload and postreload which will be invoked immediately before and after reloading the script file, respectively. Either method can be used to reinitialize your objects during reloading in any desired way. The only difference between the two methods is that prereload still runs in the old script, while postreload executes the new code which has just been loaded. Typically you'd use prereload to perform any required bookkeeping or state-saving before the script gets loaded, and postreload for custom initialization afterwards.

In particular, these callbacks can change any member variables, either directly or by invoking other methods. The most important use cases probably are to change the inlets and outlets variables, and to add a paint method, which can be done on the fly to reconfigure your objects accordingly. To these ends, you can just call the initialize method from postreload. To demonstrate this, in the tutorial examples you'll find a pdxtest.pd patch and pdxtest~.pd_lua script. The script has the following reload method:

Now, if you need to change the number of inlets and outlets of the object, you can just modify the definitions of inlets and outlets in the script's initialize method and reload. Easy as pie. Try it! Instructions can be found in the script.

Live coding and dsp

One caveat about using any of the above live-coding solutions in conjunction with Pd-Lua's signal processing capabilities is in order. When the Lua code of an object class gets reloaded, the existing code is replaced immediately. There isn't any kind of automatic "cross-fade" between old and new code. If you change the perform method of that class, there may well be discontinuities in the generated output signals which result in audible clicks. This won't matter much if you're just developing an object in your studio. But live on stage you may want to avoid this -- unless you accept or even cherish such glitches as part of your live performance.

There are ways to work around this issue, however. To demonstrate this, the tutorial examples include the following live-xfade.pd patch:

Live cross-fade

The foo~ and bar~ objects in this example are essentially the same, with some minor variations in the sound generation parameters. The particular sounds in this example are not important, each object just outputs a random walk of sine waves of varying frequencies with some phase distortion. But they will produce clicks when switching them abruptly, thus we need a smooth cross-fade between the two sound sources. This is handled by the luaxfade~ object from the Signals section.

What's special here is that the transitions are being triggered automatically, by the received reload messages. By these means, you can edit, say, the foo~ object while the bar~ object is playing, then save your edits and send a reload message. At this point the new code for foo~ is loaded while the cross-fade from bar~ to foo~ is initiated at the same time.

This method obviously requires some preparation and diligence when being used live on stage. Having some kind of automatic cross-fade functionality for dsp objects baked into Pd-Lua's run-time system would make this a lot easier. Maybe this can be provided by pdx.lua in a future release.

Conclusion

Congratulations! If you made it this far, you should have learned more than enough to start using Pd-Lua successfully for your own projects. You should also be able to read and understand the many examples in the Pd-Lua distribution, which illustrate all the various features in much more detail than we could muster in this introduction. You can find these in the examples folder, both in the Pd-Lua sources and the pdlua folder of your Pd installation.

The examples accompanying this tutorial (including the pdx.lua, pdlua-remote.el and pdlua-remote.pd files mentioned at the end of the pdx.lua section) are also available for your perusal in the examples subdirectory of the folder where you found this document.

Finally, I'd like to thank Claude Heiland-Allen for creating such an amazing tool, it makes programming Pd externals really easy and fun. Kudos also to Roberto Ierusalimschy for Lua, which for me is one of the best-designed, easiest to learn, and most capable multi-paradigm scripting languages there are today, while also being fast, simple, and light-weight.

\ No newline at end of file