From 2f4c14286d4d698dbff273f08ae6bd788d448e77 Mon Sep 17 00:00:00 2001 From: porres Date: Fri, 13 Sep 2024 10:07:47 -0300 Subject: [PATCH] Revert "update to pdlua 16" This reverts commit 67bc151227d6b439c34e20569515853980233834. --- CMakeLists.txt | 4 +- Source/Control/lua/Makefile | 60 - Source/Control/lua/Makefile.pdlibbuilder | 1365 --------- Source/Control/lua/README | 62 +- Source/Control/lua/debian/changelog | 117 - Source/Control/lua/debian/control | 40 - Source/Control/lua/debian/copyright | 38 - Source/Control/lua/debian/copyright_hints | 167 -- Source/Control/lua/debian/docs | 2 - Source/Control/lua/debian/gbp.conf | 3 - Source/Control/lua/debian/links | 2 - .../lua/debian/patches/cross-building.patch | 26 - .../lua/debian/patches/pd-lib-builder.patch | 19 - Source/Control/lua/debian/patches/series | 2 - Source/Control/lua/debian/rules | 36 - Source/Control/lua/debian/salsa-ci.yml | 4 - Source/Control/lua/debian/source/format | 1 - Source/Control/lua/debian/upstream/metadata | 5 - Source/Control/lua/debian/watch | 7 - Source/Control/lua/doc/graphics.txt | 72 +- Source/Control/lua/lua/lapi.c | 4 +- Source/Control/lua/lua/lauxlib.c | 28 +- Source/Control/lua/lua/lcode.c | 110 +- Source/Control/lua/lua/lcode.h | 3 + Source/Control/lua/lua/lcorolib.c | 4 +- Source/Control/lua/lua/ldebug.c | 249 +- Source/Control/lua/lua/ldebug.h | 1 - Source/Control/lua/lua/ldo.c | 24 +- Source/Control/lua/lua/ldo.h | 1 + Source/Control/lua/lua/ldump.c | 8 +- Source/Control/lua/lua/lgc.c | 29 +- Source/Control/lua/lua/lgc.h | 17 +- Source/Control/lua/lua/liolib.c | 27 +- Source/Control/lua/lua/llex.c | 2 +- Source/Control/lua/lua/llimits.h | 21 +- Source/Control/lua/lua/lmathlib.c | 41 +- Source/Control/lua/lua/lmem.c | 68 +- Source/Control/lua/lua/loadlib.c | 9 + Source/Control/lua/lua/lobject.c | 2 +- Source/Control/lua/lua/lobject.h | 18 +- Source/Control/lua/lua/lopcodes.h | 10 +- Source/Control/lua/lua/loslib.c | 34 +- Source/Control/lua/lua/lparser.c | 20 +- Source/Control/lua/lua/lstate.c | 16 +- Source/Control/lua/lua/lstate.h | 14 +- Source/Control/lua/lua/lstring.c | 13 +- Source/Control/lua/lua/lstrlib.c | 2 +- Source/Control/lua/lua/ltable.c | 41 +- Source/Control/lua/lua/ltable.h | 2 + Source/Control/lua/lua/ltests.c | 12 +- Source/Control/lua/lua/lua.c | 21 +- Source/Control/lua/lua/lua.h | 27 +- Source/Control/lua/lua/luaconf.h | 15 - Source/Control/lua/lua/lundump.c | 6 +- Source/Control/lua/lua/lundump.h | 3 +- Source/Control/lua/lua/lvm.c | 82 +- Source/Control/lua/lua/makefile | 3 +- Source/Control/lua/lua/manual/2html | 2 +- Source/Control/lua/lua/manual/manual.of | 81 +- Source/Control/lua/lua/onelua.c | 20 +- Source/Control/lua/lua/testes/all.lua | 1 - Source/Control/lua/lua/testes/api.lua | 112 +- Source/Control/lua/lua/testes/attrib.lua | 24 +- Source/Control/lua/lua/testes/big.lua | 2 +- Source/Control/lua/lua/testes/calls.lua | 44 +- Source/Control/lua/lua/testes/closure.lua | 6 +- Source/Control/lua/lua/testes/code.lua | 6 +- Source/Control/lua/lua/testes/constructs.lua | 36 +- Source/Control/lua/lua/testes/coroutine.lua | 64 +- Source/Control/lua/lua/testes/db.lua | 44 +- Source/Control/lua/lua/testes/errors.lua | 117 +- Source/Control/lua/lua/testes/events.lua | 3 - Source/Control/lua/lua/testes/files.lua | 14 +- Source/Control/lua/lua/testes/gc.lua | 42 +- Source/Control/lua/lua/testes/libs/makefile | 10 +- Source/Control/lua/lua/testes/literals.lua | 33 +- Source/Control/lua/lua/testes/locals.lua | 8 +- Source/Control/lua/lua/testes/main.lua | 54 +- Source/Control/lua/lua/testes/math.lua | 6 +- Source/Control/lua/lua/testes/nextvar.lua | 2 +- Source/Control/lua/lua/testes/pm.lua | 87 +- Source/Control/lua/lua/testes/sort.lua | 23 +- Source/Control/lua/lua/testes/strings.lua | 9 +- Source/Control/lua/lua/testes/tpack.lua | 2 +- Source/Control/lua/lua/testes/utf8.lua | 4 +- Source/Control/lua/lua/testes/vararg.lua | 18 +- Source/Control/lua/lua/testes/verybig.lua | 12 +- .../lua/luadoc/examples/complex-help.pd | 9 + .../lua/luadoc/examples/complex.pd_lua | 12 + .../lua/luadoc/examples/complex.pd_luax | 9 + .../luadoc/examples/dispatchertest-help.pd | 10 + .../lua/luadoc/examples/dispatchertest.pd_lua | 21 + .../luadoc/examples/dispatchertest.pd_luax | 13 + .../lua/luadoc/examples/dumptypes.pd_luax | 12 + .../lua/luadoc/examples/errors-help.pd | 12 + .../Control/lua/luadoc/examples/errors.pd_lua | 7 + .../lua/luadoc/examples/ldelay-help.pd | 19 + .../Control/lua/luadoc/examples/ldelay.pd_lua | 43 + .../lua/luadoc/examples/ldelay2-help.pd | 21 + .../lua/luadoc/examples/ldelay2.pd_lua | 51 + .../lua/luadoc/examples/ldemux-help.pd | 55 + .../Control/lua/luadoc/examples/ldemux.pd_lua | 28 + .../Control/lua/luadoc/examples/lexpr-help.pd | 134 + .../Control/lua/luadoc/examples/lexpr.pd_lua | 168 ++ .../lua/luadoc/examples/lfloat2bytes-help.pd | 25 + .../lua/luadoc/examples/lfloat2bytes.pd_lua | 58 + .../lua/luadoc/examples/list-pak-help.pd | 35 + .../lua/luadoc/examples/list-pak.pd_lua | 57 + .../lua/luadoc/examples/list-unpack-help.pd | 38 + .../lua/luadoc/examples/list-unpack.pd_lua | 58 + .../lua/luadoc/examples/llist-drip-help.pd | 32 + .../lua/luadoc/examples/llist-drip.pd_lua | 39 + .../lua/luadoc/examples/llist-rdrip-help.pd | 32 + .../lua/luadoc/examples/llist-rdrip.pd_lua | 39 + .../Control/lua/luadoc/examples/lpipe-help.pd | 19 + .../Control/lua/luadoc/examples/lpipe.pd_lua | 30 + .../lua/luadoc/examples/lreceive-help.pd | 23 + .../lua/luadoc/examples/lreceive.pd_lua | 44 + .../Control/lua/luadoc/examples/lsend-help.pd | 21 + .../Control/lua/luadoc/examples/lsend.pd_lua | 23 + .../lua/luadoc/examples/lsymbol-drip-help.pd | 81 + .../lua/luadoc/examples/lsymbol-drip.pd_lua | 56 + .../lua/luadoc/examples/lsymbol2bytes-help.pd | 25 + .../lua/luadoc/examples/lsymbol2bytes.pd_lua | 46 + .../lua/luadoc/examples/ltabdump-help.pd | 69 + .../lua/luadoc/examples/ltabdump.pd_lua | 36 + .../lua/luadoc/examples/ltabfill-help.pd | 21 + .../lua/luadoc/examples/ltabfill.pd_lua | 179 ++ .../luadoc/examples/ltextfile-drip-help.pd | 71 + .../lua/luadoc/examples/ltextfile-drip.pd_lua | 230 ++ .../lua/luadoc/examples/luametro-help.pd | 53 + .../lua/luadoc/examples/luametro.pd_lua | 161 + .../Control/lua/luadoc/examples/lurn-help.pd | 160 + .../Control/lua/luadoc/examples/lurn.pd_lua | 82 + .../lua/luadoc/examples/mutatee.pd_lua | 10 + .../lua/luadoc/examples/mutator-help.pd | 14 + .../lua/luadoc/examples/mutator.pd_lua | 13 + .../Control/lua/luadoc/examples/nop-help.pd | 13 + Source/Control/lua/luadoc/examples/nop.pd_lua | 11 + .../luadoc/examples/paths-example/hello.txt | 1 + .../examples/paths-example/lpaths-help.pd | 24 + .../examples/paths-example/lpaths.pd_lua | 34 + .../examples/paths-example/subdir/hello.txt | 1 + .../paths-example/subdir/lpaths.pd_lua | 34 + .../lua/luadoc/examples/peekbag-help.pd | 34 + .../lua/luadoc/examples/peekbag.pd_lua | 72 + .../lua/luadoc/examples/requirer-help.pd | 13 + .../lua/luadoc/examples/requirer.pd_lua | 21 + .../lua/luadoc/examples/revalue-help.pd | 44 + .../lua/luadoc/examples/revalue.pd_lua | 39 + .../luadoc/examples/reverb-calculator-help.pd | 75 + .../luadoc/examples/reverb-calculator.pd_lua | 52 + Source/Control/lua/luadoc/examples/reverb.pd | 149 + .../lua/luadoc/examples/shared-help.pd | 41 + .../Control/lua/luadoc/examples/shared.pd_lua | 34 + .../lua/luadoc/examples/shared.pd_luax | 33 + .../lua/luadoc/examples/simplecounter-help.pd | 36 + .../lua/luadoc/examples/simplecounter.pd_lua | 14 + .../lua/luadoc/examples/simplecounter.pd_luax | 11 + .../Control/lua/luadoc/examples/swarm-help.pd | 624 ++++ .../Control/lua/luadoc/examples/swarm.pd_lua | 100 + Source/Control/lua/luadoc/hello-gui.pd_lua | 171 ++ Source/Control/lua/luadoc/hello-help.pd | 10 + Source/Control/lua/luadoc/hello.lua | 1 + Source/Control/lua/luadoc/hello.pd_lua | 12 + .../lua/luadoc/tutorial/examples/fibs.pd | 53 + .../lua/luadoc/tutorial/examples/fibs.pd_lua | 38 + .../luadoc/tutorial/examples/foo-test-luax.pd | 14 + .../lua/luadoc/tutorial/examples/foo-test.pd | 19 + .../lua/luadoc/tutorial/examples/foo.pd_lua | 44 + .../lua/luadoc/tutorial/examples/foo.pd_luax | 12 + .../lua/luadoc/tutorial/examples/loadtest.lua | 3 + .../lua/luadoc/tutorial/examples/loadtest.pd | 4 + .../lua/luadoc/tutorial/examples/luarecv.pd | 23 + .../luadoc/tutorial/examples/luarecv.pd_lua | 30 + .../lua/luadoc/tutorial/examples/luasend.pd | 10 + .../luadoc/tutorial/examples/luasend.pd_lua | 13 + .../lua/luadoc/tutorial/examples/luatab.pd | 155 + .../luadoc/tutorial/examples/luatab.pd_lua | 51 + .../lua/luadoc/tutorial/examples/pd-remote.el | 128 + .../lua/luadoc/tutorial/examples/pd-remote.pd | 17 + .../lua/luadoc/tutorial/examples/pdx.lua | 139 + .../lua/luadoc/tutorial/examples/tictoc.pd | 30 + .../luadoc/tutorial/examples/tictoc.pd_lua | 63 + .../lua/luadoc/tutorial/pd-lua-intro.pdf | Bin 0 -> 918608 bytes Source/Control/lua/pd.lua | 341 ++- Source/Control/lua/pdlua-help.pd | 93 +- Source/Control/lua/pdlua-meta.pd | 18 +- Source/Control/lua/pdlua.c | 2629 +++++++++++++++++ Source/Control/lua/pdlua.h | 25 +- .../lua/pdlua/tutorial/examples/bar~.pd_lua | 43 - .../lua/pdlua/tutorial/examples/dial.pd | 99 - .../lua/pdlua/tutorial/examples/dial.pd_lua | 172 -- .../lua/pdlua/tutorial/examples/foo~.pd_lua | 42 - .../lua/pdlua/tutorial/examples/live-xfade.pd | 36 - .../lua/pdlua/tutorial/examples/luarecv.pd | 9 +- .../lua/pdlua/tutorial/examples/luatab.pd | 10 +- .../lua/pdlua/tutorial/examples/luatab.pd_lua | 7 +- .../lua/pdlua/tutorial/examples/luaxfade.pd | 35 - .../pdlua/tutorial/examples/luaxfade~.pd_lua | 85 - .../lua/pdlua/tutorial/examples/pdx.lua | 126 +- .../lua/pdlua/tutorial/examples/pdxtest.pd | 40 - .../pdlua/tutorial/examples/pdxtest~.pd_lua | 85 - .../lua/pdlua/tutorial/examples/tictoc.pd_lua | 2 + .../lua/pdlua/tutorial/pd-lua-intro.pdf | Bin 1548448 -> 984679 bytes .../lua/pdlua/tutorial/tutorial-meta.pd | 8 - Source/Control/lua/pdlua_gfx.h | 21 +- Source/Control/lua/pdluax-help.pd | 67 - .../lua/tutorial/15-remote-control1.png | Bin 35682 -> 47742 bytes .../lua/tutorial/15-remote-control2.png | Bin 35310 -> 0 bytes .../lua/tutorial/16-remote-control2.gif | Bin 221009 -> 238872 bytes Source/Control/lua/tutorial/17-live-xfade.png | Bin 42178 -> 0 bytes Source/Control/lua/tutorial/17-signal1.png | Bin 35766 -> 0 bytes Source/Control/lua/tutorial/17-signal2.png | Bin 49195 -> 0 bytes Source/Control/lua/tutorial/17-signal3.png | Bin 36180 -> 0 bytes Source/Control/lua/tutorial/17-signal4.png | Bin 60198 -> 0 bytes Source/Control/lua/tutorial/18-graphics1.png | Bin 26228 -> 0 bytes Source/Control/lua/tutorial/18-graphics2.png | Bin 28975 -> 0 bytes Source/Control/lua/tutorial/18-graphics3.png | Bin 29363 -> 0 bytes Source/Control/lua/tutorial/18-graphics4.png | Bin 35663 -> 0 bytes Source/Control/lua/tutorial/18-graphics5.png | Bin 38430 -> 0 bytes Source/Control/lua/tutorial/18-graphics6.png | Bin 48918 -> 0 bytes Source/Control/lua/tutorial/pd-lua-intro.html | 60 +- Source/Control/lua/tutorial/pd-lua-intro.md | 692 +---- 224 files changed, 8927 insertions(+), 4777 deletions(-) delete mode 100644 Source/Control/lua/Makefile delete mode 100644 Source/Control/lua/Makefile.pdlibbuilder delete mode 100644 Source/Control/lua/debian/changelog delete mode 100644 Source/Control/lua/debian/control delete mode 100644 Source/Control/lua/debian/copyright delete mode 100644 Source/Control/lua/debian/copyright_hints delete mode 100644 Source/Control/lua/debian/docs delete mode 100644 Source/Control/lua/debian/gbp.conf delete mode 100644 Source/Control/lua/debian/links delete mode 100644 Source/Control/lua/debian/patches/cross-building.patch delete mode 100644 Source/Control/lua/debian/patches/pd-lib-builder.patch delete mode 100644 Source/Control/lua/debian/patches/series delete mode 100755 Source/Control/lua/debian/rules delete mode 100644 Source/Control/lua/debian/salsa-ci.yml delete mode 100644 Source/Control/lua/debian/source/format delete mode 100644 Source/Control/lua/debian/upstream/metadata delete mode 100644 Source/Control/lua/debian/watch create mode 100644 Source/Control/lua/luadoc/examples/complex-help.pd create mode 100644 Source/Control/lua/luadoc/examples/complex.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/complex.pd_luax create mode 100644 Source/Control/lua/luadoc/examples/dispatchertest-help.pd create mode 100644 Source/Control/lua/luadoc/examples/dispatchertest.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/dispatchertest.pd_luax create mode 100644 Source/Control/lua/luadoc/examples/dumptypes.pd_luax create mode 100644 Source/Control/lua/luadoc/examples/errors-help.pd create mode 100644 Source/Control/lua/luadoc/examples/errors.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/ldelay-help.pd create mode 100644 Source/Control/lua/luadoc/examples/ldelay.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/ldelay2-help.pd create mode 100644 Source/Control/lua/luadoc/examples/ldelay2.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/ldemux-help.pd create mode 100644 Source/Control/lua/luadoc/examples/ldemux.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/lexpr-help.pd create mode 100644 Source/Control/lua/luadoc/examples/lexpr.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/lfloat2bytes-help.pd create mode 100644 Source/Control/lua/luadoc/examples/lfloat2bytes.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/list-pak-help.pd create mode 100644 Source/Control/lua/luadoc/examples/list-pak.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/list-unpack-help.pd create mode 100644 Source/Control/lua/luadoc/examples/list-unpack.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/llist-drip-help.pd create mode 100644 Source/Control/lua/luadoc/examples/llist-drip.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/llist-rdrip-help.pd create mode 100644 Source/Control/lua/luadoc/examples/llist-rdrip.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/lpipe-help.pd create mode 100644 Source/Control/lua/luadoc/examples/lpipe.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/lreceive-help.pd create mode 100644 Source/Control/lua/luadoc/examples/lreceive.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/lsend-help.pd create mode 100644 Source/Control/lua/luadoc/examples/lsend.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/lsymbol-drip-help.pd create mode 100644 Source/Control/lua/luadoc/examples/lsymbol-drip.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/lsymbol2bytes-help.pd create mode 100644 Source/Control/lua/luadoc/examples/lsymbol2bytes.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/ltabdump-help.pd create mode 100644 Source/Control/lua/luadoc/examples/ltabdump.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/ltabfill-help.pd create mode 100644 Source/Control/lua/luadoc/examples/ltabfill.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/ltextfile-drip-help.pd create mode 100644 Source/Control/lua/luadoc/examples/ltextfile-drip.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/luametro-help.pd create mode 100644 Source/Control/lua/luadoc/examples/luametro.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/lurn-help.pd create mode 100644 Source/Control/lua/luadoc/examples/lurn.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/mutatee.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/mutator-help.pd create mode 100644 Source/Control/lua/luadoc/examples/mutator.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/nop-help.pd create mode 100644 Source/Control/lua/luadoc/examples/nop.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/paths-example/hello.txt create mode 100644 Source/Control/lua/luadoc/examples/paths-example/lpaths-help.pd create mode 100644 Source/Control/lua/luadoc/examples/paths-example/lpaths.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/paths-example/subdir/hello.txt create mode 100644 Source/Control/lua/luadoc/examples/paths-example/subdir/lpaths.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/peekbag-help.pd create mode 100644 Source/Control/lua/luadoc/examples/peekbag.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/requirer-help.pd create mode 100644 Source/Control/lua/luadoc/examples/requirer.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/revalue-help.pd create mode 100644 Source/Control/lua/luadoc/examples/revalue.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/reverb-calculator-help.pd create mode 100644 Source/Control/lua/luadoc/examples/reverb-calculator.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/reverb.pd create mode 100644 Source/Control/lua/luadoc/examples/shared-help.pd create mode 100644 Source/Control/lua/luadoc/examples/shared.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/shared.pd_luax create mode 100644 Source/Control/lua/luadoc/examples/simplecounter-help.pd create mode 100644 Source/Control/lua/luadoc/examples/simplecounter.pd_lua create mode 100644 Source/Control/lua/luadoc/examples/simplecounter.pd_luax create mode 100644 Source/Control/lua/luadoc/examples/swarm-help.pd create mode 100644 Source/Control/lua/luadoc/examples/swarm.pd_lua create mode 100644 Source/Control/lua/luadoc/hello-gui.pd_lua create mode 100644 Source/Control/lua/luadoc/hello-help.pd create mode 100644 Source/Control/lua/luadoc/hello.lua create mode 100644 Source/Control/lua/luadoc/hello.pd_lua create mode 100644 Source/Control/lua/luadoc/tutorial/examples/fibs.pd create mode 100644 Source/Control/lua/luadoc/tutorial/examples/fibs.pd_lua create mode 100644 Source/Control/lua/luadoc/tutorial/examples/foo-test-luax.pd create mode 100644 Source/Control/lua/luadoc/tutorial/examples/foo-test.pd create mode 100644 Source/Control/lua/luadoc/tutorial/examples/foo.pd_lua create mode 100644 Source/Control/lua/luadoc/tutorial/examples/foo.pd_luax create mode 100644 Source/Control/lua/luadoc/tutorial/examples/loadtest.lua create mode 100644 Source/Control/lua/luadoc/tutorial/examples/loadtest.pd create mode 100644 Source/Control/lua/luadoc/tutorial/examples/luarecv.pd create mode 100644 Source/Control/lua/luadoc/tutorial/examples/luarecv.pd_lua create mode 100644 Source/Control/lua/luadoc/tutorial/examples/luasend.pd create mode 100644 Source/Control/lua/luadoc/tutorial/examples/luasend.pd_lua create mode 100644 Source/Control/lua/luadoc/tutorial/examples/luatab.pd create mode 100644 Source/Control/lua/luadoc/tutorial/examples/luatab.pd_lua create mode 100644 Source/Control/lua/luadoc/tutorial/examples/pd-remote.el create mode 100644 Source/Control/lua/luadoc/tutorial/examples/pd-remote.pd create mode 100644 Source/Control/lua/luadoc/tutorial/examples/pdx.lua create mode 100644 Source/Control/lua/luadoc/tutorial/examples/tictoc.pd create mode 100644 Source/Control/lua/luadoc/tutorial/examples/tictoc.pd_lua create mode 100644 Source/Control/lua/luadoc/tutorial/pd-lua-intro.pdf create mode 100644 Source/Control/lua/pdlua.c delete mode 100644 Source/Control/lua/pdlua/tutorial/examples/bar~.pd_lua delete mode 100644 Source/Control/lua/pdlua/tutorial/examples/dial.pd delete mode 100644 Source/Control/lua/pdlua/tutorial/examples/dial.pd_lua delete mode 100644 Source/Control/lua/pdlua/tutorial/examples/foo~.pd_lua delete mode 100644 Source/Control/lua/pdlua/tutorial/examples/live-xfade.pd delete mode 100644 Source/Control/lua/pdlua/tutorial/examples/luaxfade.pd delete mode 100644 Source/Control/lua/pdlua/tutorial/examples/luaxfade~.pd_lua delete mode 100644 Source/Control/lua/pdlua/tutorial/examples/pdxtest.pd delete mode 100644 Source/Control/lua/pdlua/tutorial/examples/pdxtest~.pd_lua delete mode 100644 Source/Control/lua/pdlua/tutorial/tutorial-meta.pd delete mode 100644 Source/Control/lua/pdluax-help.pd delete mode 100644 Source/Control/lua/tutorial/15-remote-control2.png delete mode 100644 Source/Control/lua/tutorial/17-live-xfade.png delete mode 100644 Source/Control/lua/tutorial/17-signal1.png delete mode 100644 Source/Control/lua/tutorial/17-signal2.png delete mode 100644 Source/Control/lua/tutorial/17-signal3.png delete mode 100644 Source/Control/lua/tutorial/17-signal4.png delete mode 100644 Source/Control/lua/tutorial/18-graphics1.png delete mode 100644 Source/Control/lua/tutorial/18-graphics2.png delete mode 100644 Source/Control/lua/tutorial/18-graphics3.png delete mode 100644 Source/Control/lua/tutorial/18-graphics4.png delete mode 100644 Source/Control/lua/tutorial/18-graphics5.png delete mode 100644 Source/Control/lua/tutorial/18-graphics6.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 25d79d5b0..94e40605d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,8 +85,8 @@ file(GLOB extra_files set(lua_resources ${CMAKE_CURRENT_SOURCE_DIR}/Source/Audio/scope3d~.pd_lua ${CMAKE_CURRENT_SOURCE_DIR}/Source/Control/lua/pd.lua - ${CMAKE_CURRENT_SOURCE_DIR}/Source/Control/lua/pdlua/hello.lua - ${CMAKE_CURRENT_SOURCE_DIR}/Source/Control/lua/pdlua/hello.pd_lua + ${CMAKE_CURRENT_SOURCE_DIR}/Source/Control/lua/luadoc/hello.lua + ${CMAKE_CURRENT_SOURCE_DIR}/Source/Control/lua/luadoc/hello.pd_lua ) file(GLOB merda_files "${CMAKE_CURRENT_SOURCE_DIR}/Abstractions/Merda/Modules/*") diff --git a/Source/Control/lua/Makefile b/Source/Control/lua/Makefile deleted file mode 100644 index 791024ef1..000000000 --- a/Source/Control/lua/Makefile +++ /dev/null @@ -1,60 +0,0 @@ -# -*- mode: makefile-gmake -*- - -# This needs GNU make. - -# You may want to set these if you have the Pd include files in a -# non-standard location, and/or want to install the external in a -# custom directory. - -#PDINCLUDEDIR = /usr/include/pd -#PDLIBDIR = /usr/lib/pd/extra - -# No need to edit anything below this line, usually. - -lib.name = pdlua - -pdlua_version := $(shell git describe --tags 2>/dev/null) - -luasrc = $(wildcard lua/onelua.c) - -PKG_CONFIG ?= pkg-config - -ifeq ($(luasrc),) -# compile with installed liblua -$(info ++++ NOTE: using installed lua) -luaflags = $(shell $(PKG_CONFIG) --cflags lua) -lualibs = $(shell $(PKG_CONFIG) --libs lua) -else -# compile with Lua submodule -$(info ++++ NOTE: using lua submodule) -luaflags = -DMAKE_LIB -Ilua -define forDarwin -luaflags += -DLUA_USE_MACOSX -endef -define forLinux -luaflags += -DLUA_USE_LINUX -endef -define forWindows -luaflags += -DLUA_USE_WINDOWS -endef -endif - -cflags = ${luaflags} -DPDLUA_VERSION="$(pdlua_version)" - -pdlua.class.sources := pdlua.c $(luasrc) -pdlua.class.ldlibs := $(lualibs) - -datafiles = pd.lua $(wildcard pdlua*-help.pd) - -PDLIBBUILDER_DIR=. -include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder - -install: installplus - -pdx_files = $(addprefix ./pdlua/tutorial/examples/, pdx.lua pd-remote.el pd-remote.pd) - -installplus: - $(INSTALL_DIR) -v "$(installpath)" - cp -r ./pdlua/ "${installpath}"/pdlua - cp pdlua-meta.pd "${installpath}" - cp $(pdx_files) "${installpath}" diff --git a/Source/Control/lua/Makefile.pdlibbuilder b/Source/Control/lua/Makefile.pdlibbuilder deleted file mode 100644 index 38a1220fc..000000000 --- a/Source/Control/lua/Makefile.pdlibbuilder +++ /dev/null @@ -1,1365 +0,0 @@ -# Makefile.pdlibbuilder dated 2019-12-21 -version = 0.7.0 - -# Helper makefile for Pure Data external libraries. -# Written by Katja Vetter March-June 2015 for the public domain. No warranties. -# Inspired by Hans Christoph Steiner's Makefile Template and Stephan Beal's -# ShakeNMake. -# -# Grab the newest version of Makefile.pdlibbuilder from -# https://github.com/pure-data/pd-lib-builder/ -# -# GNU make version >= 3.81 required. -# -# -#=== characteristics =========================================================== -# -# -# - defines build settings based on autodetected OS and architecture -# - defines rules to build Pd class- or lib executables from C or C++ sources -# - defines rules for libdir installation -# - defines convenience targets for developer and user -# - evaluates implicit dependencies for non-clean builds -# -# -#=== basic usage =============================================================== -# -# -# In your Makefile, define your Pd lib name and class files, and include -# Makefile.pdlibbuilder at the end of the Makefile. Like so: -# -# ________________________________________________________________________ -# -# # Makefile for mylib -# -# lib.name = mylib -# -# class.sources = myclass1.c myclass2.c -# -# datafiles = myclass1-help.pd myclass2-help.pd README.txt LICENSE.txt -# -# include Makefile.pdlibbuilder -# ________________________________________________________________________ -# -# -# For files in class.sources it is assumed that class basename == source file -# basename. The default target builds all classes as individual executables -# with Pd's default extension for the platform. For anything more than the -# most basic usage, continue reading. -# -# -#=== list of Makefile.pdlibbuilder API variables =============================== -# -# -# Variables available for definition in your library Makefile: -# -# - lib.name -# - lib.setup.sources -# - class.sources -# - common.sources -# - shared.sources -# - .class.sources -# - .class.ldflags -# - .class.ldlibs -# - cflags -# - ldflags -# - ldlibs -# - datafiles -# - datadirs -# - makefiles -# - makefiledirs -# - externalsdir -# -# Optional multiline defines evaluated per operating system: -# -# - forLinux -# - forDarwin -# - forWindows -# -# Variables available for your makefile or make command line: -# -# - make-lib-executable -# - suppress-wunused -# -# Path variables for make command line or environment: -# -# - PDDIR -# - PDINCLUDEDIR -# - PDBINDIR -# - PDLIBDIR -# -# Standard make variables for make command line or environment: -# -# - CPPFLAGS -# - CFLAGS -# - LDFLAGS -# - CC -# - CXX -# - INSTALL -# - STRIP -# - DESTDIR -# -# Optional user variables for make command line or environment: -# -# - PLATFORM -# - extension -# - floatsize -# -# Deprecated path variables: -# -# - pdincludepath -# - pdbinpath -# - objectsdir -# -# -#=== descriptions of Makefile.pdlibbuilder API variables ======================= -# -# -# lib.name: -# Name of the library directory as it will be installed / distributed. Also the -# name of the lib executable in the case where all classes are linked into -# a single binary. -# -# lib.setup.sources: -# Source file(s) (C or C++) which must be compiled only when linking all classes -# into a single lib binary. -# -# class.sources: -# All sources files (C or C++) for which the condition holds that -# class name == source file basename. -# -# .class.sources: -# Source file(s) (C or C++) specific to class . Use this for -# multiple-source classes or when class name != source file basename. -# -# common.sources: -# Source file(s) which must be statically linked to each class in the library. -# -# shared.sources: -# Source file(s) (C or C++) to build a shared dynamic link lib, to be linked -# with all class executables. -# -# cflags, ldflags, ldlibs: -# Define cflags (preprocessor&compiler), ldflags (linker) and ldlibs (dynamic -# link libs) for the whole library. These flags are added to platform-specific -# flags defined by Makefile.pdlibbuilder. -# -# .class.ldflags and .class.ldlibs: -# Define ldflags resp. ldlibs specific to class . These flags are -# added to platform-specific flags defined by Makefile.pdlibbuilder, and flags -# defined in your Makefile for the whole library. Note: cflags can not be -# defined per class in the current implementation. -# -# datafiles and datadirs: -# All extra files you want to include in binary distributions of the -# library: abstractions and help patches, example patches, meta patch, readme -# and license texts, manuals, sound files, etcetera. Use 'datafiles' for all -# files that should go into your lib rootdir and 'datadirs' for complete -# directories you want to copy from source to distribution. -# -# forLinux, forDarwin, forWindows: -# Shorthand for 'variable definitions for Linux only' etc. Use like: -# define forLinux -# cflags += -DLINUX -# class.sources += linuxthing.c -# endef -# -# makefiles and makefiledirs: -# Extra makefiles or directories with makefiles that should be made in sub-make -# processes. -# -# make-lib-executable: -# When this variable is defined 'yes' in your makefile or as command argument, -# Makefile.pdlibbuilder will try to build all classes into a single library -# executable (but it will force exit if lib.setup.sources is undefined). -# If your makefile defines 'make-lib-executable=yes' as the library default, -# this can still be overridden with 'make-lib-executable=no' as command argument -# to build individual class executables (the Makefile.pdlibbuilder default.) -# -# suppress-wunused: -# When this variable is defined ('yes' or any other value), -Wunused-variable, -# -Wunused-parameter, -Wunused-value and -Wunused-function are suppressed, -# but the other warnings from -Wall are retained. -# -# PDDIR: -# Root directory of 'portable' pd package. When defined, PDINCLUDEDIR and -# PDBINDIR will be evaluated as $(PDDIR)/src and $(PDDIR)/bin. -# -# PDINCLUDEDIR: -# Directory where Pd API m_pd.h should be found, and other Pd header files. -# Overrides the default search path. -# -# PDBINDIR: -# Directory where pd.dll should be found for linking (Windows only). Overrides -# the default search path. -# -# PDLIBDIR: -# Root directory for installation of Pd library directories. Overrides the -# default install location. -# -# DESTDIR: -# Prepended path component for staged install. -# -# PLATFORM: -# Target platform for cross compilation in the form of GNU triplet: -# cpu-vendor-os. Example: x86_64-w64-mingw32. This specifies the tool chain that -# pdlibbuilder will use, if installed and locatable. System and architecture -# will then be autodefined accordingly. In most cases no other variables need to -# be overridden. -# -# extension: -# Extension for the external to use. Example: m_amd64 -# A sane default is picked, but it is useful if you want to provide -# co-installable externals for multiple platforms (for the same operating -# systems) -# -# floatsize: -# the size of the t_float in bits. Example: 32 -# t_float are usually single precision (32bit), which is the default. -# For double precision use floatsize=64 -# When building double precision externals, you will want to set the extension -# as well, e.g. extension=windows-amd64-64.dll (--.) -# -# CPPFLAGS: -# Preprocessor flags which are not strictly required for building. -# -# CFLAGS: -# Compiler flags which are not strictly required for building. Compiler flags -# defined by Makefile.pdlibbuilder for warning, optimization and architecture -# specification are overriden by CFLAGS. -# -# LDFLAGS: -# Linker flags which are not strictly required for building. Linker flags -# defined by Makefile.pdlibbuilder for architecture specification are overriden -# by LDFLAGS. -# -# CC and CXX: -# C and C++ compiler programs as defined in your build environment. -# -# INSTALL -# Definition of install program. -# -# STRIP -# Name of strip program. Default 'strip' can be overridden in cross compilation -# environments. -# -# objectsdir: -# Root directory for installation of Pd library directories, like PDLIBDIR but -# not overridable by environment. Supported for compatibility with pd-extended -# central makefile, but deprecated otherwise. -# -# pdincludepath, pdbinpath: -# As PDINCLUDEDIR and PDBINDIR but not overridable by environment. Deprecated -# as user variables. -# -# -#=== paths ===================================================================== -# -# -# Source files in directories other than current working directory must be -# prefixed with their relative path. Do not rely on VPATH or vpath. -# Object (.o) files are built in the directory of their source files. -# Executables are built in current working directory. -# -# Default search path for m_pd.h and other API header files is platform -# dependent, and overridable by PDINCLUDEDIR: -# -# Linux: /usr/include/pd -# -# OSX: /Applications/Pd*.app/Contents/Resources/src -# -# Windows: %PROGRAMFILES%/Pd/src -# %PROGRAMFILES(X86)%/Pd/src (32 bit builds on 64 bit Windows) -# -# Default search path for binary pd.dll (Windows), overridable by PDBINDIR -# -# %PROGRAMFILES%/Pd/bin -# %PROGRAMFILES(X86)%/Pd/bin (32 bit builds on 64 bit Windows) -# -# Default location to install pd libraries is platform dependent, and -# overridable by PDLIBDIR: -# -# Linux: /usr/local/lib/pd-externals -# OSX: ~/Library/Pd -# Windows: %APPDATA%/Pd -# -# https://puredata.info/docs/faq/how-do-i-install-externals-and-help-files -# The rationale for not installing to ~/pd-externals by default on Linux -# is that some people share the home dir between 32 and 64 bit installations. -# -# -#=== targets =================================================================== -# -# -# all: build $(executables) plus optional post target -# post: target to build after $(executables) -# alldebug: build all with -g option turned on for debug symbols -# : force clean build of an individual class -# .pre: make preprocessor output file in current working directory -# .lst: make asm/source output file in current working directory -# -# install: install executables and data files -# clean: remove build products from source tree -# -# help: print help text -# vars: print makefile variables -# allvars: print all variables -# depend: print generated prerequisites -# dumpmachine: print compiler output of option '-dumpmachine' -# coffee: dummy target -# -# Variable $(executables) expands to class executables plus optional shared lib, -# or alternatively to single lib executable when make-lib-executable=true. -# Targets pre and post can be defined by library makefile. Make sure to include -# Makefile.pdlibbuilder first so default target all will not be redefined. -# -# -#=== Pd-extended libdir concept ================================================ -# -# -# For libdir layout as conceived by Hans-Christoph Steiner, see: -# -# https://puredata.info/docs/developer/Libdir -# -# Files README.txt, LICENSE.txt and -meta.pd are part of the libdir -# convention. Help patches for each class and abstraction are supposed to be -# available. Makefile.pdlibbuilder does not force the presence of these files -# however. It does not automatically include such files in libdir installations. -# Data files you want to include in distributions must be defined explicitly in -# your Makefile. -# -# -#=== Makefile.pdlibbuilder syntax conventions ================================== -# -# -# Makefile.pdlibbuilder variable names are lower case. Default make variables, -# environment variables, and standard user variables (CC, CXX, CFLAGS, DESTDIR) -# are upper case. Use target 'allvars' to print all variables and their values. -# -# 'Fields' in data variables are separated by dots, like in 'foo.class.sources'. -# Words in variables expressing a function or command are separated by dashes, -# like in 'make-lib-executable'. -# -# -#=== useful make options ======================================================= -# -# -# Use 'make -d ' to print debug details of the make process. -# Use 'make -p ' to print make's database. -# -# -#=== TODO ====================================================================== -# -# -# - decide whether to use -static-libgcc or shared dll in MinGW -# - cygwin support -# - android support -# - figure out how to handle '$' in filenames -# - add makefile template targets dpkg-source dist libdir distclean tags? -# -# -#=== end of documentation sections ============================================= -# -# -################################################################################ -################################################################################ -################################################################################ - - -# GNU make version 3.81 (2006) or higher is required because of the following: -# - function 'info' -# - variable '.DEFAULT_GOAL' - -# force exit when make version is < 3.81 -ifneq ($(firstword $(sort 3.81 $(MAKE_VERSION))), 3.81) - $(error GNU make version 3.81 or higher is required) -endif - -# Relative path to externals root dir in multi-lib source tree like -# pd-extended SVN. Default is parent of current working directory. May be -# defined differently in including makefile. -externalsdir ?= .. - -# variable you can use to check if Makefile.pdlibbuilder is already included -Makefile.pdlibbuilder = true - - -################################################################################ -### target platform detection ################################################## -################################################################################ - - -#=== target platform =========================================================== - - -# PLATFORM: optional user variable to define target platform for cross -# compilation. Redefine build tools accordingly. PLATFORM should match -# the exact target prefix of tools present in $PATH, like x86_64-w64-mingw32, -# x86_64-apple-darwin12 etc. Tool definitions are exported to ensure submakes -# will get the same. - -ifneq ($(PLATFORM),) - ifneq ($(findstring darwin, $(PLATFORM)),) - export CC = $(PLATFORM)-cc - export CXX = $(PLATFORM)-c++ - export CPP = $(PLATFORM)-cc - else - export CC = $(PLATFORM)-gcc - export CXX = $(PLATFORM)-g++ - export CPP = $(PLATFORM)-cpp - endif - STRIP = $(PLATFORM)-strip -endif - -# Let (native or cross-) compiler report target triplet and isolate individual -# words therein to facilitate later processing. -target.triplet := $(subst -, ,$(shell $(CC) -dumpmachine)) - - -#=== operating system ========================================================== - - -# The following systems are defined: Linux, Darwin, Windows. GNU and -# GNU/kFreeBSD are treated as Linux to get the same options. - -ifneq ($(filter linux gnu% kfreebsd, $(target.triplet)),) - system = Linux -endif - -ifneq ($(filter darwin%, $(target.triplet)),) - system = Darwin -endif - -ifneq ($(filter mingw% cygwin%, $(target.triplet)),) - system = Windows -endif - -# evaluate possible system-specific multiline defines from library makefile -$(eval $(for$(system))) - - -# TODO: Cygwin, Android - - -#=== architecture ============================================================== - - -# The following CPU names can be processed by pdlibbuilder: -# i*86 Intel 32 bit -# x86_64 Intel 64 bit -# arm ARM 32 bit -# aarch64 ARM 64 bit - -target.arch := $(firstword $(target.triplet)) - - -################################################################################ -### variables per platform ##################################################### -################################################################################ - -#=== flags per floatsize == ==================================================== -floatsize = 32 -ifneq ($(filter-out 32,$(floatsize)),) - floatsize.flags = -DPD_FLOATSIZE=$(floatsize) -else - floatsize.flags = -endif - - -#=== flags per architecture ==================================================== - - -# Set architecture-dependent cflags, mainly for Linux. For Mac and Windows, -# arch.c.flags are overriden below. To see gcc's default architecture flags: -# $ gcc -Q --help=target - -# ARMv6: Raspberry Pi 1st gen, not detectable from target.arch -ifeq ($(shell uname -m), armv6l) - arch.c.flags = -march=armv6 -mfpu=vfp -mfloat-abi=hard - -# ARMv7: Beagle, Udoo, RPi2 etc. -else ifeq ($(target.arch), arm) - arch.c.flags = -march=armv7-a -mfpu=vfpv3 -mfloat-abi=hard - -# ARMv8 64 bit, not tested yet -else ifeq ($(target.arch), aarch64) - arch.c.flags = -mcpu=cortex-a53 - -# Intel 32 bit, build with SSE and SSE2 instructions -else ifneq ($(filter i%86, $(target.arch)),) - arch.c.flags = -march=pentium4 -mfpmath=sse -msse -msse2 - -# Intel/AMD 64 bit, build with SSE, SSE2 and SSE3 instructions -else ifeq ($(target.arch), x86_64) - arch.c.flags = -march=core2 -mfpmath=sse -msse -msse2 -msse3 - -# if none of the above architectures detected -else - arch.c.flags = -endif - - -#=== flags and paths for Linux ================================================= - - -ifeq ($(system), Linux) - prefix = /usr/local - libdir := $(prefix)/lib - pkglibdir = $(libdir)/pd-externals - pdincludepath := $(wildcard /usr/include/pd) - extension = pd_linux - cpp.flags := -DUNIX - c.flags := -fPIC - c.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags - c.ldlibs := -lc -lm - cxx.flags := -fPIC -fcheck-new - cxx.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags - cxx.ldlibs := -lc -lm -lstdc++ - shared.extension = so - shared.ldflags = -rdynamic -fPIC -shared -Wl,-soname,$(shared.lib) -endif - - -#=== flags and paths for Darwin ================================================ - - -# LLVM-clang doesn't support -fcheck-new, therefore this flag is only used when -# compiling with g++. - -ifeq ($(system), Darwin) - pkglibdir = $(HOME)/Library/Pd - pdincludepath := $(firstword $(wildcard \ - /Applications/Pd*.app/Contents/Resources/src)) - extension = pd_darwin - cpp.flags := -DUNIX -DMACOSX -I /sw/include - c.flags := - c.ldflags := -undefined suppress -flat_namespace -bundle - c.ldlibs := -lc - cxx.ldflags := -undefined suppress -flat_namespace -bundle - cxx.ldlibs := -lc - shared.extension = dylib - shared.ldflags = -dynamiclib -undefined dynamic_lookup \ - -install_name @loader_path/$(shared.lib) \ - -compatibility_version 1 -current_version 1.0 - ifneq ($(filter %g++, $(CXX)),) - cxx.flags := -fcheck-new - endif - ifeq ($(extension), d_fat) - arch := i386 x86_64 - else - arch := $(target.arch) - endif - ifneq ($(filter -mmacosx-version-min=%, $(cflags)),) - version.flag := $(filter -mmacosx-version-min=%, $(cflags)) - else - version.flag = -mmacosx-version-min=10.6 - endif - arch.c.flags := $(addprefix -arch , $(arch)) $(version.flag) - arch.ld.flags := $(arch.c.flags) -endif - - -#=== flags and paths for Windows =============================================== - - -# Standard paths on Windows contain spaces, and GNU make functions treat such -# paths as lists, with unintended effects. Therefore we must use shell function -# ls instead of make's wildcard when probing for a path, and use double quotes -# when specifying a path in a command argument. - -# Default paths in Mingw / Mingw-w64 environments. 'PROGRAMFILES' is standard -# location for builds with native architecture, 'ProgramFiles(x86)' for i686 -# builds on x86_64 Windows (detection method by Lucas Cordiviola). Curly braces -# required because of parentheses in variable name. -ifeq ($(system), Windows) - pkglibdir := $(APPDATA)/Pd - ifeq ($(target.arch), i686) - programfiles := ${ProgramFiles(x86)} - else - programfiles := $(PROGRAMFILES) - endif - pdbinpath := $(programfiles)/Pd/bin - pdincludepath := $(programfiles)/Pd/src -endif - -# Store default path to pd.dll in PDBINDIR if the latter is not user-defined. -# For include path this is done in the platform-independent paths section below, -# but for PDBINDIR it is done here so ld flags can be evaluated as immediate -# variables. -ifeq ($(system), Windows) - ifdef PDDIR - PDBINDIR := $(PDDIR)/bin - endif - PDBINDIR ?= $(pdbinpath) -endif - -# TODO: decide whether -mms-bitfields should be specified. -ifeq ($(system), Windows) - cpp.flags := -DMSW -DNT - ifeq ($(target.arch), i686) - arch.c.flags := -march=pentium4 -msse -msse2 -mfpmath=sse - else ifeq ($(target.arch), x86_64) - cpp.flags := -DMSW -DNT -DPD_LONGINTTYPE=__int64 - arch.c.flags := -march=core2 -msse -msse2 -msse3 -mfpmath=sse - else - arch.c.flags = - endif - extension = dll - c.flags := - c.ldflags := -static-libgcc -shared \ - -Wl,--enable-auto-import "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll" - c.ldlibs := - cxx.flags := -fcheck-new - cxx.ldflags := -static-libgcc -static-libstdc++ -shared \ - -Wl,--enable-auto-import "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll" - cxx.ldlibs := - shared.extension = dll - shared.ldflags := -static-libgcc -shared "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll" - stripflags = --strip-all -endif - - -#=== paths ===================================================================== - - -# Platform-dependent default paths are specified above, but overridable. -# Path variables in upper case can be defined as make command argument or in the -# environment. Variable 'objectsdir' is supported for compatibility with -# the build system that pd-l2ork has inherited from pd-extended. - -PDINCLUDEDIR ?= $(pdincludepath) -PDLIBDIR ?= $(firstword $(objectsdir) $(pkglibdir)) - -ifdef PDDIR - PDINCLUDEDIR := $(wildcard $(PDDIR)/src) -endif - -# base path where all components of the lib will be installed by default -installpath := $(DESTDIR)$(PDLIBDIR)/$(lib.name) - -# check if include path contains spaces (as is often the case on Windows) -# if so, store the path so we can later do checks with it -pdincludepathwithspaces := $(if $(word 2, $(PDINCLUDEDIR)), $(PDINCLUDEDIR)) - - -#=== accumulated build flags =================================================== - - -# From GNU make docs: 'Users expect to be able to specify CFLAGS freely -# themselves.' So we use CFLAGS to define options which are not strictly -# required for compilation: optimizations, architecture specifications, and -# warnings. CFLAGS can be safely overriden using a make command argument. -# Variables cflags, ldflags and ldlibs may be defined in including makefile. - -optimization.flags = -O3 -ffast-math -funroll-loops -fomit-frame-pointer -warn.flags = -Wall -Wextra -Wshadow -Winline -Wstrict-aliasing - -# suppress -Wunused-variable & Co if you don't want to clutter a build log -ifdef suppress-wunused - warn.flags += $(addprefix -Wno-unused-, function parameter value variable) -endif - -CFLAGS = $(warn.flags) $(optimization.flags) $(arch.c.flags) - -# preprocessor flags -cpp.flags := -DPD -I "$(PDINCLUDEDIR)" $(floatsize.flags) $(cpp.flags) $(CPPFLAGS) - -# flags for dependency checking (cflags from makefile may define -I options) -depcheck.flags := $(cpp.flags) $(cflags) - -# architecture specifications for linker are overridable by LDFLAGS -LDFLAGS := $(arch.ld.flags) - -# now add the same ld flags to shared dynamic lib -shared.ldflags += $(LDFLAGS) - -# accumulated flags for C compiler / linker -c.flags := $(cpp.flags) $(c.flags) $(cflags) $(CFLAGS) -c.ldflags := $(c.ldflags) $(ldflags) $(LDFLAGS) -c.ldlibs := $(c.ldlibs) $(ldlibs) - -# accumulated flags for C++ compiler / linker -cxx.flags := $(cpp.flags) $(cxx.flags) $(cflags) $(CFLAGS) -cxx.ldflags := $(cxx.ldflags) $(ldflags) $(LDFLAGS) -cxx.ldlibs := $(cxx.ldlibs) $(ldlibs) - - -################################################################################ -### variables: library name and version ######################################## -################################################################################ - - -# strip possibles spaces from lib.name, they mess up calculated file names -lib.name := $(strip $(lib.name)) - -# if meta file exists, check library version -metafile := $(wildcard $(lib.name)-meta.pd) - -ifdef metafile - lib.version := $(shell sed -n \ - 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' \ - $(metafile)) -endif - - -################################################################################ -### variables: files ########################################################### -################################################################################ - -object.extension = $(extension).o - -#=== sources =================================================================== - - -# (re)define .class.sources using file names in class.sources - -define add-class-source -$(notdir $(basename $v)).class.sources += $v -endef - -$(foreach v, $(class.sources), $(eval $(add-class-source))) - -# derive class names from .class.sources variables -sourcevariables := $(filter %.class.sources, $(.VARIABLES)) -classes := $(basename $(basename $(sourcevariables))) - -# accumulate all source files specified in makefile -classes.sources := $(sort $(foreach v, $(sourcevariables), $($v))) -all.sources := $(classes.sources) $(lib.setup.sources) \ - $(shared.sources) $(common.sources) - - -#=== object files ============================================================== - - -# construct object filenames from all C and C++ source file names -classes.objects := $(addsuffix .$(object.extension), $(basename $(classes.sources))) -common.objects := $(addsuffix .$(object.extension), $(basename $(common.sources))) -shared.objects := $(addsuffix .$(object.extension), $(basename $(shared.sources))) -lib.setup.objects := $(addsuffix .$(object.extension), $(basename $(lib.setup.sources))) -all.objects = $(classes.objects) $(common.objects) $(shared.objects) \ - $(lib.setup.objects) - - -#=== executables =============================================================== - - -# construct class executable names from class names -classes.executables := $(addsuffix .$(extension), $(classes)) - -# Construct shared lib executable name if shared sources are defined. -# If extension does not end with shared extension, use both to facilitate co- -# installation for different platforms, like .m_i386.dll and .linux-amd64-32.so -ifdef shared.sources - ifneq ($(filter %.$(shared.extension), .$(extension)), ) - # $(extension) already ends with $(shared.extension), no need to duplicate it - shared.lib = lib$(lib.name).$(extension) - else - shared.lib = lib$(lib.name).$(extension).$(shared.extension) - endif -else - shared.lib := -endif - - -################################################################################ -### variables: tools ########################################################### -################################################################################ - - -# aliases so we can later define 'compile-$1' and set 'c' or 'cxx' as argument -compile-c := $(CC) -compile-cxx := $(CXX) - - -################################################################################ -### checks ##################################################################### -################################################################################ - - -# At this point most variables are defined. Now do some checks and info's -# before rules begin. - -# print Makefile.pdlibbuilder version before possible termination -$(info ++++ info: using Makefile.pdlibbuilder version $(version)) - -# Terminate if target triplet remained empty, to avoid all sorts of confusing -# scenarios and spurious bugs. -ifeq ($(target.triplet),) - $(error Command "$(CC) -dumpmachine" did not return a target triplet, \ - needed for a build. \ - Is compiler "$(CC)" installed in your PATH? ($(PATH)). \ - Does compiler "$(CC)" support option "-dumpmachine"?) -endif - -# 'forward declaration' of default target, needed to do checks -all: - -# To avoid unpredictable results, make sure the default target is not redefined -# by including makefile. -ifneq ($(.DEFAULT_GOAL), all) - $(error Default target must be 'all'.) -endif - -# find out which target(s) will be made -ifdef MAKECMDGOALS - goals := $(MAKECMDGOALS) -else - goals := all -endif - -# store path to Pd API m_pd.h if it is found -ifdef PDINCLUDEDIR - mpdh := $(shell ls "$(PDINCLUDEDIR)/m_pd.h") -endif - -# store path to pd.dll; if not found, ls will give a useful error -ifeq ($(system), Windows) - pddll := $(shell ls "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll") -endif - -# when making target all, check if m_pd.h is found and print info about it -ifeq ($(goals), all) - $(if $(mpdh), \ - $(info ++++ info: using Pd API $(mpdh)), \ - $(warning Where is Pd API m_pd.h? Do 'make help' for info.)) -endif - -# print target info -$(info ++++ info: making target $(goals) $(if $(lib.name),in lib $(lib.name))) - -# when installing, print installpath info -$(if $(filter install install-lib, $(goals)), $(info ++++ info: \ - installpath is '$(installpath)')) - - -#=== define executables ======================================================== - - -# By default we build class executables, and optionally a shared dynamic link -# lib. When make-lib-executable=yes we build all classes into a single lib -# executable, on the condition that variable lib.setup.sources is defined. - -ifeq ($(make-lib-executable),yes) - $(if $(lib.setup.sources), ,\ - $(error Can not build library blob because lib.setup.sources is undefined)) - executables := $(lib.name).$(extension) -else - executables := $(classes.executables) $(shared.lib) -endif - - -################################################################################ -### rules: special targets ##################################################### -################################################################################ - - -# Disable built-in rules. If some target can't be built with the specified -# rules, it should not be built at all. -MAKEFLAGS += --no-builtin-rules - -.PRECIOUS: -.SUFFIXES: -.PHONY: all post build-lib \ - $(classes) $(makefiledirs) $(makefiles) \ - install install-executables install-datafiles install-datadirs \ - force clean vars allvars depend help - - -################################################################################ -### rules: build targets ####################################################### -################################################################################ - - -# Target all forces the build of targets [$(executables) post] in -# deterministic order. Target $(executables) builds class executables plus -# optional shared lib or alternatively a single lib executable when -# make-lib-executable=true. Target post is optionally defined by -# library makefile. - -all: post -post: $(executables) - -all: - $(info ++++info: target all in lib $(lib.name) completed) - -# build all with -g option turned on for debug symbols -alldebug: c.flags += -g -alldebug: cxx.flags += -g -alldebug: all - - -#=== class executable ========================================================== - - -# recipe for linking objects in class executable -# argument $1 = compiler type (c or cxx) -# argument $2 = class basename -define link-class - $(compile-$1) \ - $($1.ldflags) $($2.class.ldflags) \ - -o $2.$(extension) \ - $(addsuffix .$(object.extension), $(basename $($2.class.sources))) \ - $(addsuffix .$(object.extension), $(basename $(common.sources))) \ - $($1.ldlibs) $($2.class.ldlibs) $(shared.lib) -endef - -# general rule for linking object files in class executable -%.$(extension): $(shared.lib) - $(info ++++ info: linking objects in $@ for lib $(lib.name)) - $(if $(filter %.cc %.cpp, $($*.class.sources)), \ - $(call link-class,cxx,$*), \ - $(call link-class,c,$*)) - - -#=== library blob ============================================================== - - -# build all classes into single executable -build-lib: $(lib.name).$(extension) - $(info ++++ info: library blob $(lib.name).$(extension) completed) - -# recipe for linking objects in lib executable -# argument $1 = compiler type (c or cxx) -define link-lib - $(compile-$1) \ - $($1.ldflags) $(lib.ldflags) \ - -o $(lib.name).$(extension) $(all.objects) \ - $($1.ldlibs) $(lib.ldlibs) -endef - -# rule for linking objects in lib executable -# declared conditionally to avoid name clashes -ifeq ($(make-lib-executable),yes) -$(lib.name).$(extension): $(all.objects) - $(if $(filter %.cc %.cpp, $(all.sources)), \ - $(call link-lib,cxx), \ - $(call link-lib,c)) -endif - - -#=== shared dynamic lib ======================================================== - - -# recipe for linking objects in shared executable -# argument $1 = compiler type (c or cxx) -define link-shared - $(compile-$1) \ - $(shared.ldflags) \ - -o $(shared.lib) $(shared.objects) \ - $($1.ldlibs) $(shared.ldlibs) -endef - -# rule for linking objects in shared executable -# build recipe is in macro 'link-shared' -$(shared.lib): $(shared.objects) - $(info ++++ info: linking objects in shared lib $@) - $(if $(filter %.cc %.cpp, $(shared.sources)), \ - $(call link-shared,cxx), \ - $(call link-shared,c)) - - -#=== object files ============================================================== - - -# recipe to make .o file from source -# argument $1 is compiler type (c or cxx) -define make-object-file - $(info ++++ info: making $@ in lib $(lib.name)) - $(compile-$1) \ - $($1.flags) \ - -o $@ -c $< -endef - -# Three rules to create .o files. These are double colon 'terminal' rules, -# meaning they are the last in a rules chain. - -%.$(object.extension):: %.c - $(call make-object-file,c) - -%.$(object.extension):: %.cc - $(call make-object-file,cxx) - -%.$(object.extension):: %.cpp - $(call make-object-file,cxx) - - -#=== explicit prerequisites for class executables ============================== - - -# For class executables, prerequisite rules are declared in run time. Target -# 'depend' prints these rules for debugging purposes. - -# declare explicit prerequisites rule like 'class: class.extension' -# argument $v is class basename -define declare-class-target -$v: $v.$(extension) -endef - -# declare explicit prerequisites rule like 'class.extension: object1.o object2.o' -# argument $v is class basename -define declare-class-executable-target -$v.$(extension): $(addsuffix .$(object.extension), $(basename $($v.class.sources))) \ - $(addsuffix .$(object.extension), $(basename $(common.sources))) -endef - -# evaluate explicit prerequisite rules for all classes -$(foreach v, $(classes), $(eval $(declare-class-target))) -$(foreach v, $(classes), $(eval $(declare-class-executable-target))) - - -#=== implicit prerequisites for class executables ============================== - - -# Evaluating implicit prerequisites (header files) with help from the -# preprocessor is 'expensive' so this is done conditionally and selectively. -# Note that it is also possible to trigger a build via install targets, in -# which case implicit prerequisites are not checked. - -# When the Pd include path contains spaces it will mess up the implicit -# prerequisites rules. -disable-dependency-tracking := $(strip $(pdincludepathwithspaces)) - -ifndef disable-dependency-tracking - must-build-everything := $(filter all, $(goals)) - must-build-class := $(filter $(classes), $(goals)) - must-build-sources := $(foreach v, $(must-build-class), $($v.class.sources)) -endif - -# declare implicit prerequisites rule like 'object.o: header1.h header2.h ...' -# argument $1 is input source file(s) -# dir is explicitly added because option -MM strips it by default -define declare-object-target -$(dir $1)$(patsubst %.o:,%.$(object.extension):,$(filter %.o: %.h, $(shell $(CPP) $(depcheck.flags) -MM $1))) $(MAKEFILE_LIST) -endef - -# evaluate implicit prerequisite rules when rebuilding everything -ifdef must-build-everything - $(if $(wildcard $(all.objects)), \ - $(info ++++ info: evaluating implicit prerequisites in lib $(lib.name).....) \ - $(foreach v, $(all.sources), $(eval $(call declare-object-target, $v)))) -endif - -# evaluate implicit prerequisite rules when selectively building classes -ifdef must-build-class - $(foreach v, $(must-build-sources), \ - $(eval $(call declare-object-target, $v))) - $(foreach v, $(shared.sources), \ - $(eval $(call declare-object-target, $v))) -endif - - -################################################################################ -### rules: preprocessor and assembly files ##################################### -################################################################################ - - -# Preprocessor and assembly output files for bug tracing etc. They are not part -# of the build processes for executables. By default these files are created in -# the current working directory. Dependency tracking is not performed, the build -# is forced instead to make sure it's up to date. - -force: - - -#=== preprocessor file ========================================================= - - -# make preprocessor output file with extension .pre -# argument $1 = compiler type (c or cxx) -define make-preprocessor-file - $(info ++++ info: making preprocessor output file $(notdir $*.pre) \ - in current working directory) - $(compile-$1) -E $< $(c.flags) $($1.flags) -o $(notdir $*.pre) -endef - -%.pre:: %.c force - $(call make-preprocessor-file,c) - -%.pre:: %.cc force - $(call make-preprocessor-file,cxx) - -%.pre:: %.cpp force - $(call make-preprocessor-file,cxx) - - -#=== assembly file ============================================================= - - -# make C / assembly interleaved output file with extension .lst -# argument $1 = compiler type (c or cxx) -define make-assembly-file - $(info ++++ info: making assembly output file $(notdir $*.lst) \ - in current working directory) - $(compile-$1) \ - -c -Wa,-a,-ad -fverbose-asm \ - $($1.flags) \ - $< > $(notdir $*.lst) -endef - -%.lst:: %.c force - $(call make-assembly-file,c) - -%.lst:: %.cc force - $(call make-assembly-file,cxx) - -%.lst:: %.cpp force - $(call make-assembly-file,cxx) - - -################################################################################ -### rules: installation targets ################################################ -################################################################################ - - -#=== strip ===================================================================== - - -# Stripping of installed binaries will only be done when variable 'stripflags' -# is defined non-empty. No default definition is provided except for Windows -# where the unstripped binaries are large, especially in the case of Mingw-w64. - -# Note: while stripping all symbols ('-s' or '--strip-all') is possible for -# Linux and Windows, in the case of OSX only non-global symbols can be stripped -# (option '-x' or '--discard-all'). - -# Make definition of strip command overridable so it can be defined in an -# environment for cross-compilation. -STRIP ?= strip - -# Commands in 'strip-executables' will be executed conditionally in the rule for -# target 'install-executables'. -strip-executables = cd "$(installpath)" && \ - $(foreach v, $(executables), $(STRIP) $(stripflags) '$v';) - - -#=== install =================================================================== - - -# Install targets depend on successful exit status of target all because nothing -# must be installed in case of a build error. - -# -p = preserve time stamps -# -m = set permission mode (as in chmod) -# -d = create all components of specified directories -INSTALL = install -INSTALL_PROGRAM := $(INSTALL) -p -m 644 -INSTALL_DATA := $(INSTALL) -p -m 644 -INSTALL_DIR := $(INSTALL) -m 755 -d - -# strip spaces from file names -executables := $(strip $(executables)) -datafiles := $(strip $(datafiles)) -datadirs := $(strip $(datadirs)) - -# Do not make any install sub-target with empty variable definition because the -# install program would exit with an error. -install: $(if $(executables), install-executables) -install: $(if $(datafiles), install-datafiles) -install: $(if $(datadirs), install-datadirs) - -install-executables: all - $(INSTALL_DIR) -v "$(installpath)" - $(foreach v, $(executables), \ - $(INSTALL_PROGRAM) '$v' "$(installpath)";) - $(info ++++ info: executables of lib $(lib.name) installed \ - from $(CURDIR) to $(installpath)) - $(if $(stripflags), $(strip-executables),) - -install-datafiles: all - $(INSTALL_DIR) -v "$(installpath)" - $(foreach v, $(datafiles), \ - $(INSTALL_DATA) '$(v)' "$(installpath)";) - $(info ++++ info: data files of lib $(lib.name) installed \ - from $(CURDIR) to $(installpath)) - -install-datadirs: all - $(foreach v, $(datadirs), $(INSTALL_DIR) "$(installpath)/$v";) - $(foreach v, $(datadirs), \ - $(INSTALL_DATA) $(wildcard $v/*) "$(installpath)/$v";) - $(info ++++ info: data directories of lib $(lib.name) installed \ - from $(CURDIR) to $(installpath)) - - -################################################################################ -### rules: distribution targets ################################################ -################################################################################ - - -# TODO -# These targets are implemented in Makefile Template, but I have to figure out -# how to do it under the not-so-strict conditions of Makefile.pdlibbuilder. - -# make source package -dist: - @echo "target dist not yet implemented" - -# make Debian source package -dpkg-source: - @echo "target dpkg-source not yet implemented" - -$(ORIGDIR): - -$(DISTDIR): - - -################################################################################ -### rules: clean targets ####################################################### -################################################################################ - - -# delete build products from build tree -clean: - rm -f $(all.objects) - rm -f $(classes.executables) $(lib.name).$(extension) $(shared.lib) - rm -f *.pre *.lst - -# remove distribution directories and tarballs from build tree -distclean: clean - @echo "target distclean not yet implemented" - - -################################################################################ -### rules: submake targets ##################################################### -################################################################################ - - -# Iterate over sub-makefiles or makefiles in other directories. - -# When 'continue-make=yes' is set, sub-makes will report 'true' to the parent -# process regardless of their real exit status. This prevents the parent make -# from being aborted by a sub-make error. Useful when you want to quickly find -# out which sub-makes from a large set will succeed. -ifeq ($(continue-make),yes) - continue = || true -endif - -# These targets will trigger sub-make processes for entries in 'makefiledirs' -# and 'makefiles'. -all alldebug install clean distclean dist dkpg-source: \ - $(makefiledirs) $(makefiles) - -# this expands to identical rules for each entry in 'makefiledirs' -$(makefiledirs): - $(MAKE) --directory=$@ $(MAKECMDGOALS) $(continue) - -# this expands to identical rules for each entry in 'makefiles' -$(makefiles): - $(MAKE) --directory=$(dir $@) --makefile=$(notdir $@) $(MAKECMDGOALS) $(continue) - - -################################################################################ -### rules: convenience targets ################################################# -################################################################################ - - -#=== show variables ============================================================ - - -# Several 'function' macro's cause errors when expanded within a rule or without -# proper arguments. Variables which are set with the define directive are only -# shown by name for that reason. -functions = \ -add-class-source \ -declare-class-target \ -declare-class-executable-target \ -declare-object-target \ -link-class \ -link-lib \ -link-shared \ -make-object-file \ -make-preprocessor-file \ -make-assembly-file - - -# show variables from makefiles -vars: - $(info ++++ info: showing makefile variables:) - $(foreach v,\ - $(sort $(filter-out $(functions) functions, $(.VARIABLES))),\ - $(if $(filter file, $(origin $v)),\ - $(info variable $v = $($v)))) - $(foreach v, $(functions), $(info 'function' name: $v)) - @echo - -# show all variables -allvars: - $(info ++++ info: showing default, automatic and makefile variables:) - $(foreach v, \ - $(sort $(filter-out $(functions) functions, $(.VARIABLES))), \ - $(info variable ($(origin $v)) $v = $($v))) - $(foreach v, $(functions), $(info 'function' name: $v)) - @echo - - -#=== show dependencies ========================================================= - - -# show generated prerequisites rules -depend: - $(info ++++ info: generated prerequisite rules) - $(foreach v, $(classes), $(info $(declare-class-target))) - $(foreach v, $(classes), $(info $(declare-class-executable-target))) - $(foreach v, $(all.sources), $(info $(call declare-object-target, $v))) - @echo - - -#=== show help text ============================================================ - - -# brief info about targets and paths - -ifdef mpdh - mpdhinfo := $(mpdh) -else - mpdhinfo := m_pd.h was not found. Is Pd installed? -endif - -help: - @echo - @echo " Main targets:" - @echo " all: build executables (default target)" - @echo " install: install all components of the library" - @echo " vars: print makefile variables for troubleshooting" - @echo " allvars: print all variables for troubleshooting" - @echo " help: print this help text" - @echo - @echo " Pd API m_pd.h:" - @echo " $(mpdhinfo)" - @echo " You may specify your preferred Pd include directory as argument" - @echo " to the make command, like 'PDINCLUDEDIR=path/to/pd/src'." - @echo - @echo " Path for installation of your libdir(s):" - @echo " $(PDLIBDIR)" - @echo " Alternatively you may specify your path for installation as argument" - @echo " to the make command, like 'PDLIBDIR=path/to/pd-externals'." - @echo - @echo " Default paths are listed in the doc sections in Makefile.pdlibbuilder." - @echo - - -#=== platform test ============================================================= - - -# This target can be used to test if the compiler for specified PLATFORM is -# correctly defined and available. - -dumpmachine: - @$(CC) -dumpmachine - - -#=== dummy target ============================================================== - - -coffee: - @echo "Makefile.pdlibbuilder: Can not make coffee. Sorry." - - -################################################################################ -### end of rules sections ###################################################### -################################################################################ - - -# for syntax highlighting in vim and github -# vim: set filetype=make: - diff --git a/Source/Control/lua/README b/Source/Control/lua/README index 9216d331b..80400971d 100644 --- a/Source/Control/lua/README +++ b/Source/Control/lua/README @@ -27,8 +27,8 @@ corresponding subdirectories). Originally written by Claude Heiland-Allen, pdlua has gone through the hands of a few people over the years, including mrpeach (maintainer since 2011), zmoelnig a.k.a. umlaeute (loader update, Debian package), and myself (Arch package, Lua 5.3+ support, Purr Data and -plugdata support, pdx.lua live-coding extension, tutorial). Please also check -my brief account on the history of pd-lua below. +plugdata support, tutorial). Please also check my brief account on the history +of pd-lua below. Lua 5.4 is highly recommended with the latest version, Lua 5.3 works as well. Reportedly, Lua 5.2 and even 5.1 still work (at least to some extent), @@ -42,10 +42,9 @@ https://agraef.github.io/pd-lua/tutorial/pd-lua-intro.html This contains a fairly gentle introduction to pdlua, walks you through the creation of a basic example, and then goes on to cover most major features of -pdlua, including the special facilities for tables, clocks, receivers, signal -processing, graphics, and live-coding, in quite some detail. This will be -helpful when embarking on your own projects, or trying to make sense of the -included examples. +pdlua, including the special facilities for tables, clocks, receivers, and +live-coding, in quite some detail. This will be helpful when embarking on your +own projects, or trying to make sense of the included examples. History and Credits: @@ -67,11 +66,11 @@ in turn was based on the 0.7.3 version of pdlua in Pd's svn repository. The current version (0.8 and later) is actively maintained by Albert Gräf, with contributions by Claude Heiland-Allen (Lua 5.4 update, bugfixes), -Alexandre Porres (documentation updates), sebshader (relative path loading), -Timothy Schoen (plugdata compatibility, signal and graphics support), and Ben -Wesch (Deken tests and uploads on GitHub). This is also the version included -in Purr Data (https://github.com/agraef/purr-data) and Timothy Schoen's -plugdata (https://github.com/plugdata-team/plugdata). +Alexandre Porres (documentation updates, Deken package), sebshader (relative +path loading), and Timothy Schoen (plugdata compatibility, signal and graphics +support). This is also the version included in Purr Data +(https://github.com/agraef/purr-data) and Timothy Schoen's plugdata +(https://github.com/plugdata-team/plugdata). Please file bugs and requests at https://github.com/agraef/pd-lua. I maintain a lot of packages and thus it may take a while at times, but I look at all the @@ -80,21 +79,17 @@ bug reports and pull requests, and try to respond to them eventually. :) Ready-Made Packages: -The latest vanilla binaries for Ubuntu, Mac, and Windows can always be found -on https://github.com/agraef/pd-lua (but see below for the Deken package). -Also, both Purr Data and plugdata ship with pdlua, and have it enabled by -default, so no 3rd party package is needed in those environments. +The latest vanilla binaries for Ubuntu, Mac and Windows can always be found on +https://github.com/agraef/pd-lua. The same version is also in the Arch +community repository (maintained by dvzrv, thanks David!). Other packages are +available in Debian (maintained by IOhannes Zmölnig) and Deken (maintained by +Alexandre Porres). You can also compile pdlua yourself from source, which +isn't hard to do (see below). Also, both Purr Data and plugdata ship with +pdlua, so no 3rd party package is needed in those environments. -For vanilla Pd, we recommend installing the Deken package (named `pdlua`), -readily available using Pd's `Find externals` menu option. This package was -originally uploaded by Alexandre Porres, but Ben Wesch recently added Deken -tests and uploads to our GitHub workflow, so that the Deken releases are now -automatized and always in sync with the GitHub releases. Thanks, Ben! - -pd-lua is also in the Arch repositories (maintained by dvzrv, thanks David!), -and in the Debian repositories (maintained by IOhannes Zmölnig, thanks -umlaeute!). (During busy times, these may trail our releases for a little bit, -but most of the time they will the latest version.) +To enable the pdlua loader in Pd after installation, just add `pdlua` to your +startup libraries (after adding its parent directory to Pd's search path if +necessary) and you should be set. Mac users please note that the packages I distribute aren't notarized, so on recent macOS versions you'll have to jump through the usual hoops to make them @@ -103,25 +98,8 @@ usually do the trick, but check the internet for up-to-date information on Gatekeeper for details. -Enabling pdlua: - -Again, this step is only necessary with vanilla Pd; both Purr Data and -plugdata have the pdlua loader enabled by default, so it should be ready to go -immediately. - -With vanilla Pd, after installing pdlua, the pdlua loader also needs to be -added to your startup libraries, before Pd will recognize any Lua object in -your patches. This only needs to be done once. (Alternatively, you can also -use the `-lib pdlua` option on the Pd command line, or a `declare -lib pdlua` -object in a patch with Lua objects.) To make any of this work, you may also -have to add the parent directory of the `pdlua` folder to Pd's search path if -pdlua was installed in an unusual location. - - Compilation Instructions: -You can also compile pdlua yourself from source, which isn't hard to do. - The source should compile out of the box on (at least) Linux, macOS and Windows, just make sure that you have Lua and Pd installed and run `make`. pdlua uses pd-lib-builder by Katja Vetter as its build system, please diff --git a/Source/Control/lua/debian/changelog b/Source/Control/lua/debian/changelog deleted file mode 100644 index 5bca24636..000000000 --- a/Source/Control/lua/debian/changelog +++ /dev/null @@ -1,117 +0,0 @@ -pd-lua (0.11.6+ds1-1) unstable; urgency=medium - - * New upstream version 0.11.6+ds1 - + Drop obsolete patches - + Switch buildsystem to pd-lib-builder - + Add patch for cross-building - + B-D on liblua5.4-dev - * Build for both single and double-precision Pd - * Repack upstream sources - + Exclude repository config from source tarball - + Exclude generated documentation - + Add '+ds' repacksuffix - * Update upstream information - + Update upstream contact - + Watch new upstream repository - * Update copyright information - + Update d/copyright - + Exclude binary files from 'licensecheck' - + Re-generate d/copyright_hints - * Apply 'wrap-and-sort -ast' - * Bump standards version to 4.6.2 - - -- IOhannes m zmölnig (Debian/GNU) Wed, 05 Jul 2023 15:02:17 +0200 - -pd-lua (0.7.3-6) unstable; urgency=medium - - * Modernize 'licensecheck' target - + Ensure that 'licensecheck' is run with the C.UTF-8 locale - + Exclude debian/ from 'licensecheck' - + Re-generate d/copyright_hints - - -- IOhannes m zmölnig (Debian/GNU) Wed, 07 Dec 2022 10:47:01 +0100 - -pd-lua (0.7.3-5) unstable; urgency=medium - - * Apply "warp-and-sort -ast" - * Bump standards version to 4.6.1 - - -- IOhannes m zmölnig (Debian/GNU) Sun, 04 Sep 2022 13:55:19 +0200 - -pd-lua (0.7.3-4) unstable; urgency=medium - - [ Debian Janitor ] - * Set upstream metadata fields: Archive, Bug-Database, Bug-Submit, - Repository, Repository-Browse. - - [ IOhannes m zmölnig (Debian/GNU) ] - * Add salsa-ci configuration - * Bump dh-compat to 13 - * Bump standards version to 4.5.0 - - -- IOhannes m zmölnig (Debian/GNU) Wed, 19 Aug 2020 21:48:05 +0200 - -pd-lua (0.7.3-3) unstable; urgency=medium - - [ Ondřej Nový ] - * Use debhelper-compat instead of debian/compat - - [ IOhannes m zmölnig ] - * Fix FTCBFS - + Supply pkg-config from dpkg's buildtools.mk - + Don't strip during make install. - Thanks to Helmut Grohne (Closes: #930944) - * Declare that building this package doesn't require "root" powers - * Remove obsolete d/source/local-options - * Bump dh compat to 12 - * Bump standards version to 4.4.0 - - -- IOhannes m zmölnig (Debian/GNU) Wed, 11 Sep 2019 15:40:38 +0200 - -pd-lua (0.7.3-2) unstable; urgency=medium - - * Switched buildsystem from dh to cdbs - * Bumped dh compat to 11 - * Enabled hardening - * Fixed spelling of 'Lua' - * Updated Vcs-* stanzas to salsa.d.o - * Updated maintainer address - * Switched URLs to https:// - * Removed obsolete d/git-tuneclone.sh - * Dropped d/README.source from docs - * Updated d/copyright_hints - * Bumped standards version to 4.1.3 - - -- IOhannes m zmölnig (Debian/GNU) Thu, 01 Feb 2018 23:19:43 +0100 - -pd-lua (0.7.3-1) unstable; urgency=medium - - * Imported Upstream version 0.7.3 - - * Removed patches applied upstream - * Backported patch to fix format-security errors - * Updated files to install - * Updated build-flags to new upstream build-system - * Override build-date for reproducible builds - * Depend on 'puredata' rather than 'puredata-core' - * Update Vcs-* stanzas - * Added debian/git-tuneclone.sh script (see debian/README.source) - * Regenerated debian/(control|copyright_hints) - * Updated debian/copyright - * Bumped Standards-version to 3.9.6 - - -- IOhannes m zmölnig (Debian/GNU) Sun, 24 Jan 2016 22:25:22 +0100 - -pd-lua (0.6.0-2) unstable; urgency=medium - - * Added "-fPIC" to build-flags (Closes: #759830) - * Fixed installation path for examples/ - * Document that ignoring debian/control.in is no show-stopper - - -- IOhannes m zmölnig (Debian/GNU) Mon, 01 Sep 2014 09:32:15 +0200 - -pd-lua (0.6.0-1) unstable; urgency=medium - - * Initial release. (Closes: #755268) - - -- IOhannes m zmölnig (Debian/GNU) Mon, 21 Jul 2014 18:13:24 +0200 diff --git a/Source/Control/lua/debian/control b/Source/Control/lua/debian/control deleted file mode 100644 index a193c5212..000000000 --- a/Source/Control/lua/debian/control +++ /dev/null @@ -1,40 +0,0 @@ -Source: pd-lua -Priority: optional -Maintainer: Debian Multimedia Maintainers -Uploaders: - IOhannes m zmölnig (Debian/GNU) , -Build-Depends: - debhelper-compat (= 13), - devscripts, - dh-buildinfo, - dh-sequence-pd-lib-builder, - liblua5.4-dev, - pkg-config, -Standards-Version: 4.6.2 -Section: sound -Homepage: https://download.puredata.info/pdlua -Vcs-Git: https://salsa.debian.org/multimedia-team/pd/pd-lua.git -Vcs-Browser: https://salsa.debian.org/multimedia-team/pd/pd-lua -Rules-Requires-Root: no - -Package: pd-lua -Architecture: any -Depends: - pd-libdir, - ${misc:Depends}, - ${puredata:Depends}, - ${shlibs:Depends}, -Recommends: - ${puredata:Recommends}, -Suggests: - ${puredata:Suggests}, -Description: Lua bindings for Pure Data - pd-lua adds a new loader to Pure Data (Pd), that allows one to load - objectclasses written in the popular Lua programming language. - . - Some programming problems are complicated to express in graphical - dataflow languages like Pd, requiring the user to ressort to objectclasses - written in more general purpose languages. - For those who do not want to use compiled languages like C/C++, this package - offers the possibility to write first-class objectclasses in the lightweight - programming language "Lua". diff --git a/Source/Control/lua/debian/copyright b/Source/Control/lua/debian/copyright deleted file mode 100644 index bb25ba769..000000000 --- a/Source/Control/lua/debian/copyright +++ /dev/null @@ -1,38 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: pdlua -Upstream-Contact: Albert Gräf -Source: https://github.com/agraef/pd-lua/ -Files-Excluded: - .git* - tutorial/pd-lua-intro.html - pdlua/tutorial/pd-lua-intro.pdf - -Files: * -Copyright: - 2007-2009, Claude Heiland-Allen - 2007, Frank Barknecht - 2011-2014, Martin Peach - 2014-2016, IOhannes m zmölnig - 2017-2023, Albert Gräf -License: GPL-2+ - -Files: debian/* -Copyright: 2014-2023, IOhannes m zmölnig -License: GPL-2+ - -License: GPL-2+ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - . - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -Comment: - On Debian systems, the complete text of the GNU General Public - License version 2 can be found in "/usr/share/common-licenses/GPL-2". - . - You should have received a copy of the GNU General Public License - along with this program. If not, see . diff --git a/Source/Control/lua/debian/copyright_hints b/Source/Control/lua/debian/copyright_hints deleted file mode 100644 index 773e221cc..000000000 --- a/Source/Control/lua/debian/copyright_hints +++ /dev/null @@ -1,167 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: FIXME -Upstream-Contact: FIXME -Source: FIXME -Disclaimer: Autogenerated by licensecheck - -Files: Makefile - Makefile.pdlibbuilder - doc/Makefile - doc/internal.txt - doc/lua.txt - doc/luax.txt - doc/pdlua.tex - pdlua-help.pd - pdlua-meta.pd - pdlua/examples/complex-help.pd - pdlua/examples/complex.pd_lua - pdlua/examples/complex.pd_luax - pdlua/examples/dispatchertest-help.pd - pdlua/examples/dispatchertest.pd_lua - pdlua/examples/dispatchertest.pd_luax - pdlua/examples/dumptypes.pd_luax - pdlua/examples/errors-help.pd - pdlua/examples/errors.pd_lua - pdlua/examples/ldelay-help.pd - pdlua/examples/ldelay.pd_lua - pdlua/examples/ldelay2-help.pd - pdlua/examples/ldelay2.pd_lua - pdlua/examples/ldemux-help.pd - pdlua/examples/ldemux.pd_lua - pdlua/examples/lexpr-help.pd - pdlua/examples/lexpr.pd_lua - pdlua/examples/lfloat2bytes-help.pd - pdlua/examples/lfloat2bytes.pd_lua - pdlua/examples/list-pak-help.pd - pdlua/examples/list-pak.pd_lua - pdlua/examples/list-unpack-help.pd - pdlua/examples/list-unpack.pd_lua - pdlua/examples/llist-drip-help.pd - pdlua/examples/llist-drip.pd_lua - pdlua/examples/llist-rdrip-help.pd - pdlua/examples/llist-rdrip.pd_lua - pdlua/examples/lpipe-help.pd - pdlua/examples/lpipe.pd_lua - pdlua/examples/lreceive-help.pd - pdlua/examples/lreceive.pd_lua - pdlua/examples/lsend-help.pd - pdlua/examples/lsend.pd_lua - pdlua/examples/lsymbol-drip-help.pd - pdlua/examples/lsymbol-drip.pd_lua - pdlua/examples/lsymbol2bytes-help.pd - pdlua/examples/lsymbol2bytes.pd_lua - pdlua/examples/ltabdump-help.pd - pdlua/examples/ltabdump.pd_lua - pdlua/examples/ltabfill-help.pd - pdlua/examples/ltabfill.pd_lua - pdlua/examples/ltextfile-drip-help.pd - pdlua/examples/ltextfile-drip.pd_lua - pdlua/examples/luametro-help.pd - pdlua/examples/luametro.pd_lua - pdlua/examples/lurn-help.pd - pdlua/examples/lurn.pd_lua - pdlua/examples/mutatee.pd_lua - pdlua/examples/mutator-help.pd - pdlua/examples/mutator.pd_lua - pdlua/examples/nop-help.pd - pdlua/examples/nop.pd_lua - pdlua/examples/paths-example/hello.txt - pdlua/examples/paths-example/lpaths-help.pd - pdlua/examples/paths-example/lpaths.pd_lua - pdlua/examples/paths-example/subdir/hello.txt - pdlua/examples/paths-example/subdir/lpaths.pd_lua - pdlua/examples/peekbag-help.pd - pdlua/examples/peekbag.pd_lua - pdlua/examples/requirer-help.pd - pdlua/examples/requirer.pd_lua - pdlua/examples/revalue-help.pd - pdlua/examples/revalue.pd_lua - pdlua/examples/reverb-calculator-help.pd - pdlua/examples/reverb-calculator.pd_lua - pdlua/examples/reverb.pd - pdlua/examples/shared-help.pd - pdlua/examples/shared.pd_lua - pdlua/examples/shared.pd_luax - pdlua/examples/simplecounter-help.pd - pdlua/examples/simplecounter.pd_lua - pdlua/examples/simplecounter.pd_luax - pdlua/examples/swarm-help.pd - pdlua/examples/swarm.pd_lua - pdlua/hello-help.pd - pdlua/hello.lua - pdlua/hello.pd_lua - pdlua/hello.pd_luax - pdlua/tutorial/examples/fibs.pd - pdlua/tutorial/examples/fibs.pd_lua - pdlua/tutorial/examples/foo-test-luax.pd - pdlua/tutorial/examples/foo-test.pd - pdlua/tutorial/examples/foo.pd_luax - pdlua/tutorial/examples/loadtest.lua - pdlua/tutorial/examples/loadtest.pd - pdlua/tutorial/examples/luarecv.pd - pdlua/tutorial/examples/luarecv.pd_lua - pdlua/tutorial/examples/luasend.pd - pdlua/tutorial/examples/luasend.pd_lua - pdlua/tutorial/examples/luatab.pd - pdlua/tutorial/examples/luatab.pd_lua - pdlua/tutorial/examples/pd-remote.pd - pdlua/tutorial/examples/tictoc.pd - pdlua/tutorial/examples/tictoc.pd_lua - pdluax-help.pd -Copyright: NONE -License: UNKNOWN - FIXME - -Files: tutorial/pd-lua-intro.md -Copyright: Foo software. - Foo software.") -License: CC-BY-SA-4.0 - FIXME - -Files: pdlua/tutorial/examples/pd-remote.el -Copyright: 2023, Albert Graef -License: Expat - FIXME - -Files: COPYING -Copyright: 1989, 1991, Free Software Foundation, Inc. -License: GPL-2 - FIXME - -Files: pdlua/tutorial/examples/pdx.lua -Copyright: 2020, Albert Gräf -License: GPL-2+ - FIXME - -Files: README -Copyright: 2007-2009, 2013, Claude Heiland-Allen - 2012, Martin Peach martin.peach@sympatico.ca -License: GPL-2+ - FIXME - -Files: pd.lua -Copyright: 2007-2008, Claude Heiland-Allen - 2012, Martin Peach martin.peach@sympatico.ca - if nil ~= pd._clocks[c] then -License: GPL-2+ - FIXME - -Files: pdlua.c -Copyright: 2007-2008, Claude Heiland-Allen - class_addmethod(c, (t_method)pdlua_menu_open, gensym("menu-open"), A_NULL); (mrpeach 20111025) - clock_unset(c->clock); -License: GPL-2+ - FIXME - -Files: pdlua/tutorial/examples/foo.pd_lua -Copyright: Foo software.") -License: UNKNOWN - FIXME - -Files: tutorial/pd-lua-intro.html -Copyright: Foo software.")
This will print the message just once, right after the first foo object is created. There's another finalize method which can be used to perform any kind of cleanup when an object gets destroyed. For instance, let's rework our example so that it keeps track of the actual number of foo objects, and prints an additional message when the last foo object is deleted. To these ends, we turn foo.init into a counter which keeps track of the number of foo objects:

Lua errors

We all make mistakes. It's inevitable that you'll run into errors in the Lua code you wrote, so let's finally discuss how those mishaps are handled. Pd-Lua simply reports errors from the Lua interpreter in the Pd console. For instance, suppose that we mistyped pd.post as pd_post in the code for the one-time welcome message above. You'll see an error message like this in the console:
In this case the error happened in the initialize method, so the object couldn't actually be created, and you will have to correct the typo before going on. Fortunately, the message tells us exactly where the error occurred, so we can fix it easily. Syntax errors anywhere in the script file will be caught and handled in a similar fashion.Runtime errors in inlet methods, on the other hand, will allow your objects to be created and to start executing; they just won't behave as expected and cause somewhat cryptic errors to be printed in the console. For instance, let's suppose that you forgot the curly braces around the float value in self:outlet (a fairly common error), so that the method reads:
Lua is a dynamically-typed language, so this little glitch becomes apparent only when you actually send a bang message to the object, which causes the following errors to be logged in the console:
Ok, so the first message tells us that somewhere Pd-Lua expected a table but got a non-table value. The second message actually comes from the C routine deep down in the bowls of Pd-Lua which does the actual output to an outlet. If you see that message, it's a telltale sign that you tried to output an atom not properly wrapped in a Lua table, but it gives no indication of where that happened either, other than that you can use "Find Last Error" to locate the object which caused the problem.It goes without saying that the Pd-Lua developers could have chosen a better error message there. Well, at least we now have an idea what happened and in which object, but we may then still have to start peppering our code with pd.post calls in order to find (and fix) the issue.

Inlets and outlets

As we've already seen, the number of inlets and outlets is set with the inlets and outlets member variables in the initialize method of an object. You can set these to any numbers you want, including zero. (In the current implementation, fractional numbers will be truncated to integers, and negative numbers will be treated as zero. If the variables aren't set at all, they also default to zero.)

Inlets

Let's have a look at the inlets first. Pd-Lua supports a number of different forms of inlet methods which enable us to process any kind of Pd message. In the following list, "1" stands for any literal inlet number (counting the inlets from left to right, starting at 1), and "sym" for any symbol denoting either one of the predefined Pd message types (bang, float, symbol, pointer, and list), or any other (selector) symbol at the beginning of a Pd meta message. Note that, as usual, in your code these methods are always prefixed with the class name, using Lua colon syntax.
  • in_1_sym(...) matches the given type or selector symbol on the given inlet; the method receives zero or one arguments (denoted ... here), depending on the selector symbol sym, see below
  • in_n_sym(n, ...) (with a verbatim "n" replacing the inlet number) matches the given type or selector symbol on any inlet; the actual inlet number is passed as the first argument (denoted n here), along with the zero or one extra arguments ... which, like above, depend on the selector symbol sym
  • in_1(sel, atoms) matches any type or selector symbol on the given inlet; the type or selector symbol is passed as a string sel, and the remaining arguments of the message are passed as a Lua table atoms containing number and string values
  • in_n(n, sel, atoms) matches any type or selector symbol on any inlet; the method is invoked with the inlet number n along with type/selector symbol sel and the remaining message arguments in the Lua table atoms
These alternatives are tried in the indicated order, i.e., from most specific to most general. In addition, Pd-Lua understands the following specific sym type selectors and adjusts the number and type of the extra ... arguments accordingly:
  • bang denotes a bang message and passes no arguments
  • float denotes a Pd float value, which is passed as a number argument
  • symbol denotes a Pd symbol, which is passed as a string argument
  • pointer denotes a Pd pointer, which is passed as a Lua userdata argument
  • list denotes a Pd list, which is passed as a Lua table argument containing all the list elements
  • any other sym value is taken as a literal Pd symbol to be matched against the selector symbol of the incoming message; the remaining arguments of the message are passed as a Lua table argument
Note that there can only be zero or one additional arguments in this case (besides the inlet number for in_n_sym). In contrast, the two most generic kinds of methods, in_1 and in_n, always have the type/selector symbol sel (a string) and the remaining message arguments atoms (a Lua table) as arguments.Among these, the methods for bang, float, and list are probably the most frequently used, along with in_1 or in_n as a catch-all method for processing any other kind of input message. We've already employed the in_1_bang method in our basic example above. Here are some (rather contrived) examples for the other methods; we'll see some real examples of some of these later on.

The obligatory test patch:

Receive example

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, in this section we are going to discuss 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 programming environments like Pd and Lua. Musicians also like to employ it to modify their algorithmic composition programs live on stage, which is where the term "live coding" comes from. You'll probably be using live coding a lot while developing your Pd-Lua externals, but I've kept this topic for the final section of this guide, because it requires a good understanding of Pd-Lua's basic features. So without any further ado, let's dive right into it now.

First, we need to describe the predefined Pd-Lua object classes pdlua and pdluax, so that you know what's readily available. But we'll 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 method, which is also what both pdlua and pdluax use internally.

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 is considered Pd-Lua's main workhorse for live coding, it also 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 built-in 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, you don't have to worry about these intricacies, but they 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 code doesn't break if we later change the directory structure.

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:

  • self:dofilex(scriptname): This loads the given Lua script, like Lua's loadfile, but also performs a search on Pd's path to locate the file, and finally executes the file if it was loaded successfully. Note that self must be a valid Pd-Lua object, which is used solely to determine the externdir of the object's class, so that the script will be found if it is located there.

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

Remote control

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? And maybe even do that remotely from the editor, using the pdsend program?

Well, all this is in fact possible, but the implementation is a bit too involved to fully present it here. So we have provided this in a separate pdx.lua module, which you can find in the sources accompanying this tutorial.

Setting up an object for this kind of remote control is easy, though. First, you need to import the pdx module into your script, using Lua's require:

Then just call pdx.reload(self) somewhere in the initialize method. This will set up the whole receiver/dofilex machinery in a fully automatic manner. Finally, add a message like this to your patch, which goes to the special pdluax receiver (note that this is completely 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, provided they have been set up with pdx.reload. 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 reload message in a suitable way, e.g.:

Remote control 1

You can then use pdsend 4711 localhost udp to transmit the reload message to Pd when needed. You probably don't want to run those commands yourself, but a 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 in the accompanying examples which shows how to do this. Once you've added this to your .emacs, you can just type Ctrl+C Ctrl+K in Emacs to make Pd reload your Lua script after saving it. It doesn't get much easier than that. Moreover, for your convenience I've added a little abstraction named pd-remote.pd which takes care of the netreceive and messaging bits and will look much tidier in your patches.


NOTE: To use these facilities in your own patches, you'll have to copy pdx.lua and pd-remote.pd to your project directory or some other place where Pd finds them. The pd-remote.el file can be installed in your Emacs site-lisp directory if needed. 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're not familiar with Emacs, or just prefer to use VS Code as an editor.


So here's the full source code of our reworked luatab example (now with the in_1_reload handler removed and the pdx.reload call added to the initialize method):

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 2

So there you have it: three (or rather four) different ways to live-code with Pd-Lua. Choose whatever best fits your purpose and is the most convenient for you.

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 previous section) are also available for your perusal in the examples subdirectory of the folder where you found this document.

But wait, there's more! As briefly mentioned in the introduction, the most recent Pd-Lua version also provides new facilities for audio signal processing and graphics. As these features are still fairly new, they are not yet covered in this tutorial. But you can find corresponding examples under pdlua/examples in the source or in the help browser. Specifically, check the sig-example folder for a simple example illustrating the use of signal inlets and outlets, and gui-example as well as osci3d for some GUI and graphics examples.

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 diff --git a/Source/Control/lua/tutorial/pd-lua-intro.md b/Source/Control/lua/tutorial/pd-lua-intro.md index 0d2ef78c6..39ae0d9e2 100644 --- a/Source/Control/lua/tutorial/pd-lua-intro.md +++ b/Source/Control/lua/tutorial/pd-lua-intro.md @@ -1,26 +1,21 @@ ---- -author: Albert Gräf -title: A Quick Introduction to Pd-Lua -footer: ${title} - ${pageNo} / ${pageCount} ---- # A Quick Introduction to Pd-Lua Albert Gräf \<\> Computer Music Dept., Institute of Art History and Musicology Johannes Gutenberg University (JGU) Mainz, Germany -September 2024 +July 2024 -This document is licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). Other formats: [Markdown](https://github.com/agraef/pd-lua/blob/master/tutorial/pd-lua-intro.md) source, [PDF](https://agraef.github.io/pd-lua/tutorial/pd-lua-intro.pdf) +This document is licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). Other formats: [Markdown](https://github.com/agraef/pd-lua/blob/master/tutorial/pd-lua-intro.md) source, [PDF](https://github.com/agraef/pd-lua/blob/master/pdlua/tutorial/pd-lua-intro.pdf) Permanent link: ## 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](https://www.lua.org/) 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](http://www.puc-rio.br/), 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. +Enter Pd-Lua, the Pd programmer's secret weapon, which lets you develop your externals in the [Lua](https://www.lua.org/) 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](http://www.puc-rio.br/), 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. -Pd-Lua was originally designed for control processing, so we used to recommend [Faust](https://faust.grame.fr/) 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.) +Using Pd-Lua, you can program your own externals ranging from little helper objects to full-blown sequencers and algorithmic composition tools. Pd-Lua is primarily aimed at control objects at this time (for doing dsp, you might consider using [Faust](https://faust.grame.fr/) instead), but thanks to contributions by Timothy Schoen, the most recent version also provides some support for signal processing and even graphics. It also 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. 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](https://www.lua.org/docs.html) available, and maybe have a look at Derek Banas' [video tutorial](https://www.youtube.com/watch?v=iMacxZQMPXs) for a quick overview of Lua. For Pd, we recommend the Pd FLOSS Manual at https://flossmanuals.net/ to get started. @@ -33,8 +28,8 @@ With vanilla Pd, you can install the pdlua package from Deken. There's also an o 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): ~~~ -pdlua 0.12.4 (GPL) 2008 Claude Heiland-Allen, 2014 Martin Peach et al. -pdlua: compiled for pd-0.55 on Aug 24 2024 00:51:01 +pdlua 0.11.3 (GPL) 2008 Claude Heiland-Allen, 2014 Martin Peach et al. +pdlua: compiled for pd-0.53 on Jan 16 2023 23:36:09 Using lua version 5.4 ~~~ @@ -146,23 +141,7 @@ So let's relaunch Pd, reload the patch again, and add some GUI elements to test ![Basic example](03-basic-example.png) -Note that this is still a very basic example. While the example is complete and fully functional, we have barely scratched the surface here. Pd-Lua also allows you to process an object's creation arguments (employing the `atoms` parameter of the `initialize` method, which we didn't use above), log messages and errors in the Pd console, create handlers for different types of input messages and signals, output data and signals to different outlets, work with Pd arrays, clocks, and receivers, and even do some graphics and live coding. We will dive into each of these topics in the rest of this tutorial. - -## Objects and Shortcuts - -You may have wondered about the whole `pd.Class:new():register("foo")` thing, why is that so complicated? The answer is that Lua uses *prototypes* (class-less objects) to define object-oriented data structures and their methods. Consequently, Pd-Lua defines a few of these to represent various kinds of Pd objects on the Lua side. There's `pd.Class` which represents Pd object classes, `pd.Table` which is used to access Pd arrays and tables, and `pd.Clock`, `pd.Receive` for Pd's clock and receiver objects. - -Each kind of object needs a way to create an instance of the object, that's what the `new()` method is for, and a second method (usually called `register`, but Pd arrays use `sync` instead) which associates the Lua object with its counterpart in Pd. The `register()` method also creates the corresponding data structure in Pd in most cases. - -So that's why we first invoke `new()` and then `register()` on the prototype (`pd.Class`, etc.); it's always this two-step process. However, since version 0.12.14, Pd-Lua also provides some convenient *shortcuts*. In the case of `pd.Class:new():register()`, the shortcut is simply `pd.class()`. (Note that `class` is written in all lower-case, while the prototype `pd.Class` has `Class` capitalized -- this may be hard to see in the font we've chosen for this document.) - -Thus, we can abbreviate the definition of the `foo` object class in our first example as: - -~~~lua -local foo = pd.class("foo") -~~~ - -Much shorter, and easy to remember. Shortcuts for the other kinds of objects will be introduced along with the long forms in the corresponding sections below. Feel free to use them. But note that in the tutorial and the accompanying examples we generally keep to the long forms, so that our examples will work unchanged with older Pd-Lua versions. +Note that this is still a very basic example. While the example is complete and fully functional, we have barely scratched the surface here. Pd-Lua also allows you to process an object's creation arguments (employing the `atoms` parameter of the `initialize` method, which we didn't use above), log messages and errors in the Pd console, create handlers for different types of input messages, output data to different outlets, work with Pd arrays, clocks, and receivers, and even do some live coding. We will dive into each of these topics in the following sections. ## Where your Lua files go @@ -170,13 +149,7 @@ As already mentioned, the externals (.pd_lua files) themselves can go either int The Lua loader temporarily sets up Lua's `package.path` so that it includes the directory with the external, so you can put any Lua modules (.lua files) required by the external into that directory. ---- - -**NOTE:** As of Pd-Lua 0.12.5, the same is true for the pdlua external directory, where you can put any Lua modules that should be readily available to all your Pd-Lua objects. One example of this is Pd-Lua's own [pdx.lua](#pdx.lua) live-coding module which we'll discuss later, but it can also be used for general Lua utility modules that you want to use. There are a lot of incredibly useful Lua libraries out there, such as [kikito 's inspect](https://github.com/kikito/inspect.lua) or [lunarmodules' penlight](https://github.com/lunarmodules/Penlight), although you might prefer to install these using [LuaRocks](https://luarocks.org/), Lua's most popular package manager. - ---- - -If you need/want to use Lua libraries from other locations which aren't on the standard Lua `package.path`, then you'll have to set up the `LUA_PATH` environment variable accordingly. LuaRocks usually takes care of this for you when set up properly. Otherwise you can set `LUA_PATH` manually in your system startup files, such as ~/.bashrc or ~/.xprofile on Linux. E.g.: +If you need/want to use Lua libraries from other locations (which aren't on the standard Lua `package.path`), then you'll have to set up the `LUA_PATH` environment variable accordingly. When using [LuaRocks](https://luarocks.org/), Lua's most popular package manager, it usually takes care of this for you when set up properly. Otherwise you can set `LUA_PATH` manually in your system startup files, such as ~/.bashrc or ~/.xprofile on Linux. E.g.: ~~~shell export LUA_PATH=~/lua/'?.lua;;' @@ -344,18 +317,17 @@ Thanks for using foo! ### Lua errors -We all make mistakes. It's inevitable that you'll run into errors in the Lua code you wrote, so let's finally discuss how those mishaps are handled. Pd-Lua simply reports errors from the Lua interpreter in the Pd console. For instance, suppose that we omitted the `pd` qualifier in the one-time welcome message above, turning `pd.post` into just `post` (quite an easy mistake to make). You'll then see an error message like this in the console: +We all make mistakes. It's inevitable that you'll run into errors in the Lua code you wrote, so let's finally discuss how those mishaps are handled. Pd-Lua simply reports errors from the Lua interpreter in the Pd console. For instance, suppose that we mistyped `pd.post` as `pd_post` in the code for the one-time welcome message above. You'll see an error message like this in the console: ~~~ -error: lua: foo.pd_lua: 29: attempt to call a nil value (global 'post') +error: pdlua_new: error in constructor for `foo': +[string "foo"]:7: attempt to call a nil value (global 'pd_post') error: couldn't create "foo" ~~~ -You'll recall that an undefined global like the mistyped `post` symbol above will always yield the `nil` value in Lua, which obviously isn't a function and thus can't be called like one. (Not all error messages will be quite as perspicuous, so a web search may be in order to figure them out.) In this case the error happened in the `postinitialize` method, so the object couldn't actually be created, and you will have to correct the typo before going on. Fortunately, the message tells us exactly where the error occurred, so we can fix it easily. +In this case the error happened in the `initialize` method, so the object couldn't actually be created, and you will have to correct the typo before going on. Fortunately, the message tells us exactly where the error occurred, so we can fix it easily. Syntax errors anywhere in the script file will be caught and handled in a similar fashion. -Syntax errors anywhere in the script file will be caught and handled in a similar fashion. - -Runtime errors in inlet methods, on the other hand, will allow your objects to be created and to start executing. They just won't behave as expected and cause a variety of error messages to be printed in the console which might look a bit cryptic at first glance. For instance, let's suppose that you forgot the curly braces around the float value in `self:outlet` (another fairly common error), so that the method reads: +Runtime errors in inlet methods, on the other hand, will allow your objects to be created and to start executing; they just won't behave as expected and cause somewhat cryptic errors to be printed in the console. For instance, let's suppose that you forgot the curly braces around the float value in `self:outlet` (a fairly common error), so that the method reads: ~~~lua function foo:in_1_bang() @@ -364,16 +336,17 @@ function foo:in_1_bang() end ~~~ -Lua is a dynamically-typed language, so this little glitch becomes apparent only when you actually send a `bang` message to the object, which causes the following error to be logged in the console: +Lua is a dynamically-typed language, so this little glitch becomes apparent only when you actually send a bang message to the object, which causes the following errors to be logged in the console: ~~~ -error: foo.pd_lua: 41: error: invalid atoms table [outlet 1] +error: lua: error: not a table ... click the link above to track it down, or click the 'Find Last Error' item in the Edit menu. +error: lua: error: no atoms?? ~~~ -This message actually comes from the C routine deep down in Pd-Lua which checks the arguments of the `outlet` method for validity before it sends any data through the outlet. In this case it took issue with the `atoms` argument. If you see that message, it's a telltale sign that you tried to output an atom not properly wrapped in a Lua table. The message also tells you about the outlet number which caused the issue, and the line number with the definition of the method in which the error happened (which is the `foo:in_1_bang` method in this case). So it's not the *exact* location of the `self:outlet()` call, but still close enough so that you can easily find that spot with the extra information provided in the error message. +Ok, so the first message tells us that *somewhere* Pd-Lua expected a table but got a non-table value. The second message actually comes from the C routine deep down in the bowls of Pd-Lua which does the actual output to an outlet. If you see that message, it's a telltale sign that you tried to output an atom not properly wrapped in a Lua table, but it gives no indication of where that happened either, other than that you can use "Find Last Error" to locate the object which caused the problem. -To actually fix the error, simply go to the line number from the error message in the editor to find the method with the error, and edit the `outlet` call to add the curly braces around `self.counter` back in. In case you already closed the editor, recall that you can just click on the error message (or use the "Find Last Error" operation) to locate the object, then right-click the object and open it. +It goes without saying that the Pd-Lua developers could have chosen a better error message there. Well, at least we now have an idea what happened and in which object, but we may then still have to start peppering our code with `pd.post` calls in order to find (and fix) the issue. ## Inlets and outlets @@ -530,11 +503,11 @@ And here you can see the object running in a little test patch which outputs the Pd's arrays provide an efficient means to store possibly large vectors of float values. These are often used for sample data (waveforms) of a given size (the number of samples), but can also be employed to store copious amounts of numerical control data. Arrays are usually associated with a graphical display (called a *graph*), and Pd's table object lets you create an array along with a graph as a special kind of subpatch. -Pd-Lua provides `pd.Table` to represent array and table data, and a few functions to query and manipulate that data. This comes in handy, e.g., if you want to fill an array with a computed waveform. While Pd has its own corresponding facilities, complicated waveforms are often much easier to create in Lua, which offers a fairly complete set of basic mathematical functions in its standard library, and a whole lot more through 3rd party libraries such as [Numeric Lua](https://github.com/michal-h21/numlua). +Pd-Lua provides a `Table` class to represent array and table data, and a few functions to query and manipulate that data. This comes in handy, e.g., if you want to fill an array with a computed waveform. While Pd has its own corresponding facilities, complicated waveforms are often much easier to create in Lua, which offers a fairly complete set of basic mathematical functions in its standard library, and a whole lot more through 3rd party libraries such as [Numeric Lua](https://github.com/carvalho/numlua). Here are the array/table functions provided by Pd-Lua. Note that like in Pd arrays, indices are zero-based and thus range from `0` to `tab:length()-1`. -- `pd.Table:new():sync(name)`, short form: `pd.table(name)`: creates the Lua representation of a Pd array and associates it with the Pd array named `name`. This requires that an array or table of that name already exists (`sync` will *not* create it), otherwise the result is `nil`. You usually assign that value to a local variable (named `tab` below) to refer to it later. +- `pd.Table:new():sync(name)`: creates the Lua representation of a Pd array and associates it with the Pd array named `name`. The result is `nil` if an array or table of that name doesn't exist. You usually assign that value to a local variable (named `tab` below) to refer to it later. - `tab:length()`: returns the length of `tab` (i.e., the number of samples in it) @@ -607,27 +580,23 @@ In the same vein, the Pd-Lua distribution includes a much more comprehensive exa Clocks are used internally in Pd to implement objects which "do things" when a timeout occurs, such as delays, pipes, and metronomes. Pd-Lua exposes this functionality so that objects written in Lua can do the same. The following functions are provided: -- `pd.Clock:new():register(self, method)`, short form `pd.clock(self, method)`: This creates a new clock for the Pd-Lua object `self` which, when it goes off, runs the method specified as a string `method`. Let's say that `method` is `"trigger"`, then `self:trigger()` will be called without arguments when the clock times out. You usually want to assign the result (a `pd.Clock` object) to a member variable of the object (called `self.clock` below), so that you can refer to it later. +- `pd.Clock:new():register(self, method)`: This creates a new clock for the Pd-Lua object `self` which, when it goes off, runs the method specified as a string `method`. Let's say that `method` is `"trigger"`, then `self:trigger()` will be called without arguments when the clock times out. You usually want to assign the result (a `pd.Clock` object) to a member variable of the object (called `self.clock` below), so that you can refer to it later. - `self.clock:delay(time)`: sets the clock so that it will go off (and call the clock method) after `time` milliseconds -- `self.clock:set(systime)`: sets the clock so that it will go off at the specified absolute `systime` (measured in Pd time units of `pd.TIMEUNITPERMSEC`, see below) +- `self.clock:set(systime)`: sets the clock so that it will go off at the specified absolute `systime` (measured in Pd "ticks", whatever that means) - `self.clock:unset()`: unsets the clock, canceling any timeout that has been set previously -- `self.clock:destruct()`: destroys the clock - +- `self.clock:destruct()`: destroys the clock; this is to be called in the `finalize` method, so that the clock doesn't go off (trying to invoke an invalid object) after an object was deleted ---- +We mention in passing that you can call `self.clock:delay(time)` as soon as the clock has been created, even in the `initialize` method of an object. Furthermore, you can have as many clocks as you want in the same object, carrying out different actions, as long as you assign each clock to a different method. -**NOTE:** Calling `self.clock:destruct()` on a clock that isn't needed any more (in the `finalize` method at the latest) used to be mandatory, and is still possible, but since 0.12.12 it is no longer necessary, as it also happens automatically when the target object gets deleted. This makes it now possible to have one-shot timers like `pd.Clock:new():register(self, "oneshot"):delay(1000)` which aren't assigned to any variable. (This is not recommended, though, if you want to maintain compatibility with earlier Pd-Lua versions.) +Presumably, the `self.clock:destruct()` method should also be invoked automatically when setting `self.clock` to `nil`, but the available documentation isn't terribly clear on this. So we recommend explicitly calling `self.clock:destruct()` in `self:finalize` to be on the safe side, as the documentation advises us to do, because otherwise "weird things will happen." ---- +Also note that `self.clock:set()` isn't terribly useful right now, because it refers to Pd's internal "systime" clock which isn't readily available in Pd-Lua. - -We mention in passing that you can call `self.clock:delay(time)` as soon as the clock has been created, even in the `initialize` method of an object. Furthermore, you can have as many clocks as you want in the same object, carrying out different actions, as long as you assign each clock to a different method. - -Here is a little `tictoc` object we came up with for illustration purposes. +With these caveats in mind, here is a little `tictoc` object we came up with for illustration purposes, along with the usual test patch. ~~~lua local tictoc = pd.Class:new():register("tictoc") @@ -646,6 +615,8 @@ function tictoc:initialize(sel, atoms) return true end +-- don't forget this, or else... + function tictoc:finalize() self.clock:destruct() end @@ -693,30 +664,19 @@ function tictoc:tictoc() end ~~~ -And here is the usual test patch: - ![Clock example](08-clock.png) - - -An explanation of `self.clock:set(systime)` is still in order. In contrast to `self.clock:delay(time)`, this schedules a timeout at the given *absolute* time given in `pd.TIMEUNITPERMSEC` units. This may be preferable in some situations, e.g., if you have to schedule some events that use increasing timestamps instead of delta times. Version 0.12.12 of Pd-Lua adds the `pd.TIMEUNITPERMSEC` constant and some support functions for dealing with these time values: - -- `pd.systime()`: Returns the current time in `pd.TIMEUNITPERMSEC` units. You can use this to derive a value for `self.clock:set`'s `systime` argument. For instance, `self.clock:set(pd.systime() + time * pd.TIMEUNITPERMSEC)` is equivalent to `self.clock:delay(time)`. -- `pd.timesince(systime)`: Returns the time since the given `systime` in msec. You might use this to measure the time between two events, like Pd's built-in `timer` object does. In particular, `pd.timesince(0)` gives you the msec equivalent of `pd.systime()`, i.e., Pd's current time in msec. - -Also note that the notion of time alluded to above is Pd's internal or *logical* time which always advances monotonically (until it wraps around) with each dsp tick, i.e., once per 64 samples with Pd's default block size setting. - More comprehensive examples using clocks can be found in the Pd-Lua distribution; have a look, e.g., at ldelay.pd_lua and luametro.pd_lua. Also, lpipe.pd_lua demonstrates how to dynamically create an entire collection of clocks in order to implement a delay line for a stream of messages. ## Using receivers As every seasoned Pd user knows, Pd also enables you to transmit messages in a wireless fashion, using receiver symbols, or just *receivers* for short, as destination addresses. In Pd, this is done through the built-in `send` and `receive` objects, as well as the "send" and "receive symbol" properties of GUI objects. -Sending messages to a receiver is straightforward: +Sending messages to a receiver in Pd-Lua is straightforward: - `pd.send(sym, sel, atoms)`: Sends a message with the given selector symbol `sel` (a string) and arguments `atoms` (a Lua table, which may be empty if the message has no arguments) to the given receiver `sym` (a string). -This works pretty much like the `outlet` method, but outputs messages to the given receiver instead. For instance, let's say you have a toggle with receiver symbol `onoff` in your patch, then you can turn on that toggle with a call like `pd.send("onoff", "float", {1})`. (Recall that the `atoms` argument always needs to be a table, even if it is a singleton, lest you'll get that "invalid atoms table" error that we discussed earlier). +This works pretty much like the `outlet` method, but outputs messages to the given receiver instead. For instance, let's say you have a toggle with receiver symbol `onoff` in your patch, then you can turn on that toggle with a call like `pd.send("onoff", "float", {1})`. (Recall that the `atoms` argument always needs to be a table, even if it is a singleton, lest you'll get that dreaded "no atoms??" error that we discussed earlier). One complication are receiver symbols using a `$0-` patch id prefix, which are commonly used to differentiate receiver symbols in different toplevel patches or abstractions, in order to prevent name clashes. A Pd-Lua object doesn't have any idea of what toplevel patch it is located in, and what the numeric id of that patch is, so you'll have to expand the `$0-` prefix on the Pd side and pass it, e.g., as a creation argument. For instance, suppose that the toggle receiver is in fact named `$0-onoff`, then something like the following Pd-Lua object will do the trick, if you invoke it as `luasend $0-onoff`: @@ -742,9 +702,11 @@ It is worth noting here that the same technique applies whenever you need to pas So let's have a look at receivers now. These work pretty much like clocks in that you create them registering a method, and destroy them when they are no longer needed: -- `pd.Receive:new():register(self, sym, method)`, short form `pd.receive(self, sym, method)`: This creates a new receiver named `sym` (a string) for the Pd-Lua object `self` which, when a message for that receiver becomes available, runs the method specified as a string `method`. Let's say that `method` is `"receive"`, then `self:receive(sel, atoms)` will be invoked with the selector symbol `sel` and arguments `atoms` of the transmitted message. You want to assign the result (a `pd.Receive` object) to a member variable of the object (called `self.recv` below), so that you can refer to it later (if only to destroy it, see below). +- `pd.Receive:new():register(self, sym, method)`: This creates a new receiver named `sym` (a string) for the Pd-Lua object `self` which, when a message for that receiver becomes available, runs the method specified as a string `method`. Let's say that `method` is `"receive"`, then `self:receive(sel, atoms)` will be invoked with the selector symbol `sel` and arguments `atoms` of the transmitted message. You want to assign the result (a `pd.Receive` object) to a member variable of the object (called `self.recv` below), so that you can refer to it later (if only to destroy it, see below). + +- `self.recv:destruct()`: destroys the receiver -- `self.recv:destruct()`: destroys the receiver (as with clocks, this is now entirely optional, but it may still be useful if the receiver needs to be removed before its target object ceases to be) +Note that the same caveat applies to receivers as in the case of clocks. That is, you should use the `destruct` method to destroy receivers in the `finalize` routine of the receiving object, so that they don't hang around when their object is long dead. Otherwise, you guessed it, "weird things will happen." Here is a little example which receives any kind of message, stores it, and outputs the last stored message when it gets a `bang` on its inlet. @@ -785,463 +747,11 @@ The obligatory test patch: ![Receive example](10-receive.png) -## Signals and graphics - -So far all of our examples only did control processing, which is what Pd-Lua was originally designed for. But thanks to the work of Timothy Schoen (the creator and main developer of [plugdata](https://plugdata.org/)), as of version 0.12.0 Pd-Lua also provides facilities for audio signal processing and graphics. It goes without saying that these capabilities vastly extend the scope of Pd-Lua applications, as you can now program pretty much any kind of Pd object in Lua, covering both signal and control processing, as well as custom GUI objects. We'll first discuss how to write a signal processing (a.k.a. dsp) object in Pd-Lua, and then go on to show the implementation of a simple GUI object using the graphics API. - ---- - -**NOTE:** As these features are still fairly new, some details of the implementation may still be subject to change in future Pd-Lua versions. Also, we can't cover all the details here, so we recommend having a look at the examples included in the Pd-Lua distribution. You can find these under pdlua/examples in the source or in the help browser. Specifically, check the sig-example folder for another example illustrating the use of signal inlets and outlets, and Benjamin Wesch's [osci3d~](https://github.com/ben-wes/scope3d-) which shows how to implement a 3d oscilloscope employing both signal processing and graphics. - ---- - -### Signals - -Enabling signal processing in a Pd-Lua object involves three ingredients: - -1. **Adding signal inlets and outlets:** As before, this is done by setting the `inlets` and `outlets` member variables in the `initialize` method. But instead of setting each variable to just a number, you specify a *signature*, which is a table indicating the signal and control in- and outlets with the special `SIGNAL` and `DATA` values. The number of in- and outlets is then given by the size of these tables. Thus, e.g., you'd use `self.inlets = { SIGNAL, SIGNAL, DATA }` if you need two signal and one control data inlet, in that order. Note that a number as the value of `inlets` or `outlets` corresponds to a signature with just `DATA` values in it. -2. **Adding a dsp method:** This step is optional. The `dsp` method gets invoked whenever signal processing is turned on in Pd, passing two parameters: `samplerate` and `blocksize`. The former tells you about the sample rate (number of audio samples per second) Pd runs at, which will be useful if your object needs to translate time and frequency values from physical units (i.e., seconds, milliseconds, and Hz) to sample-based time and frequency values, so usually you want to store the given value in a member variable of your object. The latter specifies the block size, i.e., the number of samples Pd expects to be processed during each call of the `perform` method (see below). You only need to store that number if your object doesn't have any signal inlets, so that you know how many samples need to be generated. Otherwise the block size can also be inferred from the size of the `in` tables passed to the `perform` method. Adding the `dsp` method is optional. You only have to define it if the signal and control data processing in your object requires the `samplerate` and `blocksize` values, or if you need to be notified when dsp processing gets turned on for some other reason. -3. **Adding a perform method:** This method is where the actual signal processing happens. It receives blocks of signal data from the inlets through its arguments, where each block is represented as a Lua table containing floating point sample values. The method then needs to return a tuple of similar Lua tables with the blocks of signal data for each outlet. Note that the number of *arguments* of the method matches the number of signal *inlets*, while the number of *return values* corresponds to the number of signal *outlets*. The `perform` method is *not* optional; if your object outputs any signal data, the method needs to be implemented, otherwise you'll get a `perform: function should return a table` or similar error in the Pd console as soon as you turn on dsp processing. - -In addition to the `dsp` and `perform` methods, your object may contain any number of methods doing the usual control data processing on the `DATA` inlets. It is also possible to receive control data on the `SIGNAL` inlets; however, you won't be able to receive `float` messages, because they will be interpreted as constant signals which get passed as blocks of signal data to the `perform` method instead. - -#### Example 1: Mixing signals - -Let's take a look at a few simple examples illustrating the kind of processing the `perform` method might do. First, let's mix two signals (stereo input) down to a single (mono) output by computing the average of corresponding samples. We need two signal inlets and one signal outlet, so our `initialize` method looks like this: - -~~~lua -local foo = pd.Class:new():register("foo") - -function foo:initialize(sel, atoms) - self.inlets = {SIGNAL,SIGNAL} - self.outlets = {SIGNAL} - return true -end -~~~ - -And here's the `perform` method (in this simple example we don't need `foo:dsp()`): - -~~~lua -function foo:perform(in1, in2) - for i = 1, #in1 do - in1[i] = (in1[i]+in2[i])/2 - end - return in1 -end -~~~ - -Note that here we replaced the signal data in the `in1` table with the result, so we simply return the modified `in1` signal; no need to create a third `out` table. (This is safe because it won't actually modify any signal data outside the Lua method.) - -Easy enough. And this is how this object works in a little test patch: - -![Mixing signals](17-signal1.png) - -#### Example 2: Analyzing a signal - -A dsp object can also have no signal outlets at all if you just want to process the incoming signal data in some way and output the result through a normal control outlet. E.g., here's one (rather simplistic) way to compute the rms ([root mean square](https://en.wikipedia.org/wiki/Root_mean_square)) envelope of a signal as control data: - -~~~lua -function foo:initialize(sel, atoms) - self.inlets = {SIGNAL} - self.outlets = {DATA} - return true -end - -function foo:perform(in1) - local rms = 0 - for i = 1, #in1 do - rms = rms + in1[i]*in1[i] - end - rms = math.sqrt(rms/#in1) - self:outlet(1, "float", {rms}) -end -~~~ - -A little test patch: - -![Analyzing a signal](17-signal2.png) - -#### Example 3: Generating a signal - -Conversely, we can also have an object which converts control inputs into signal data, such as this little oscillator object which produces a sine wave: - -~~~lua -function foo:initialize(sel, atoms) - self.inlets = {DATA} - self.outlets = {SIGNAL} - self.phase = 0 - self.freq = 220 - self.amp = 0.5 - return true -end - --- message to set frequency... -function foo:in_1_freq(atoms) - self.freq = atoms[1] -end - --- ... and amplitude. -function foo:in_1_amp(atoms) - self.amp = atoms[1] -end - -function foo:perform() - local freq = self.freq -- frequency of the sine wave in Hz - local amp = self.amp -- amplitude of the sine wave (0 to 1) - - -- calculate the angular frequency (in radians per sample) - local angular_freq = 2 * math.pi * freq / self.samplerate - - local out = {} -- result table - for i = 1, self.blocksize do - out[i] = amp * math.sin(self.phase) - self.phase = self.phase + angular_freq - if self.phase >= 2 * math.pi then - self.phase = self.phase - 2 * math.pi - end - end - - return out -end -~~~ - -The obligatory test patch: - -![Generating a signal](17-signal3.png) - -#### Real-world example: Cross-fades - -Let's finally take a look at a somewhat more realistic and useful example which performs cross-fades on two incoming signals by doing linear interpolation between the input signals. A customary design for this kind of dsp object is to have a target cross-fade value (`xfade` argument) ranging from 0 (all left signal) to 1 (all right signal). We also want to be able to smoothly ramp from one cross-fade value to the next in order to avoid clicks (`time` argument), and have an initial delay until moving to the new xfade value (`delay` argument). Here is the definition of a `luaxfade~` object which does all this, including the checking of all argument values, so that we don't run into any Lua exceptions because of bad values. The example also illustrates how to receive control messages on a signal inlet (cf. the `fade` message on the left signal inlet). You can find this as luaxfade~.pd_lua in the tutorial examples. - -~~~lua -local luaxfade = pd.Class:new():register("luaxfade~") - -function luaxfade:initialize(sel, atoms) - self.inlets = {SIGNAL,SIGNAL} - self.outlets = {SIGNAL} - self.xfade = 0 - self.xfade_to = 0 - self.xfade_time = 0 - self.xfade_delay = 0 - self.time = 0 - self.ramp = 0 - return true -end - -function luaxfade:dsp(samplerate, blocksize) - self.samplerate = samplerate -end - -function luaxfade:in_1_fade(atoms) - -- If self.samplerate is not initialized, because the dsp method has not - -- been run yet, then we cannot compute the sample delay and ramp times - -- below, so we bail out, telling the user to enable dsp first. - if not self.samplerate then - self:error("luaxfade~: unknown sample rate, please enable dsp first") - return - end - local fade, time, delay = table.unpack(atoms) - if type(delay) == "number" then - -- delay time (msec -> samples) - self.xfade_delay = math.floor(self.samplerate*delay/1000) - end - if type(time) == "number" then - -- xfade time (msec -> samples) - self.xfade_time = math.floor(self.samplerate*time/1000) - end - if type(fade) == "number" then - -- new xfade value (clamp to 0-1) - self.xfade_to = math.max(0, math.min(1, fade)) - end - if self.xfade_to ~= self.xfade then - -- start a new cycle - if self.xfade_delay > 0 then - self.time, self.ramp = self.xfade_delay, 0 - elseif self.xfade_time > 0 then - self.time, self.ramp = self.xfade_time, - (self.xfade_to-self.xfade)/self.xfade_time - else - self.xfade = self.xfade_to - self.time, self.ramp = 0, 0 - end - end -end - -function luaxfade:perform(in1, in2) - local xfade, xfade_to = self.xfade, self.xfade_to - local xfade_time = self.xfade_time - local time, ramp = self.time, self.ramp - - -- loop through each sample index - for i = 1, #in1 do - -- mix (we do this in-place, using in1 for output) - in1[i] = in1[i]*(1-xfade) + in2[i]*xfade - -- update the mix if time > 0 (sample countdown) - if time > 0 then - -- update cycle is still in ptogress - if ramp ~= 0 then - xfade = xfade + ramp - end - time = time - 1 - elseif xfade_to ~= xfade then - if xfade_time > 0 then - -- start the ramp up or down - time, ramp = xfade_time, (xfade_to-xfade)/xfade_time - else - -- no xfade_time, jump to the new value immediately - xfade = xfade_to - end - end - end - - -- update internal state - self.xfade, self.time, self.ramp = xfade, time, ramp - - -- return the mixed down sample data - return in1 -end -~~~ - -And here's the luaxfade.pd patch which takes a sine wave on the left and a noise signal on the right inlet and performs cross fades with a ramp time of 500 msec and an initial delay of 200 msec. To adjust these values, just edit the `fade` message accordingly. - -![Cross-fades](17-signal4.png) - -### Graphics - -Timothy Schoen's Pd-Lua graphics API provides you with a way to equip an object with a static or animated graphical display inside its object box on the Pd canvas. Typical examples would be various kinds of wave displays, or custom GUI objects consisting of text and simple geometric shapes. To these ends, you can adjust the size of the object box to any width and height you specify. Inside the box rectangle you can then draw text and basic geometric shapes such as lines, rectangles, circles, and arbitrary paths, through stroke and fill operations using any rgb color. - -In order to enable graphics in a Pd-Lua object, you have to provide a `paint` method. This receives a graphics context `g` as its argument, which lets you set the current color, and draw text and the various different geometric shapes using that color. In addition, you can provide methods to be called in response to mouse down, up, move, and drag actions on the object, which is useful to equip your custom GUI objects with mouse-based interaction. - -Last but not least, the `set_args` method lets you store internal object state in the object's creation arguments, which is useful if you need to keep track of persistent state when storing an object on disk (via saving the patch) or when duplicating or copying objects. This can also be used with ordinary Pd-Lua objects which don't utilize the graphics API, but it is most useful in the context of custom GUI objects. - -We use a custom GUI object, a simple kind of dial, as a running example to illustrate most of these elements in the following subsections. To keep things simple, we will not discuss the graphics API in much detail here, so you may want to check the graphics subpatch in the main pdlua-help patch, which contains a detailed listing of all available methods for reference. - -#### Getting started: A basic dial object - -Let's begin with a basic clock-like dial: just a circular *face* and a border around it, on which we draw a *center point* and the *hand* (a line) starting at the center point and pointing in any direction which indicates the current *phase* angle. So this is what we are aiming for: - -![A basic dial](18-graphics1.png) - -Following the clock paradigm, we assume that a zero phase angle means pointing upwards (towards the 12 o'clock position), while +1 or -1 indicates the 6 o'clock position, pointing downwards. Phase angles less than -1 or greater than +1 wrap around. Positive phase differences denote clockwise, negative differences counter-clockwise rotation. And since we'd like to change the phase angle displayed on the dial, we add an inlet taking float values. - -Here's the code implementing the object initialization and the float inlet: - -~~~lua -local dial = pd.Class:new():register("dial") - -function dial:initialize(sel, atoms) - self.inlets = 1 - self.outlets = 0 - self.phase = 0 - self:set_size(127, 127) - return true -end - -function dial:in_1_float(x) - self.phase = x - self:repaint() -end -~~~ - -The `self:set_size()` call in the `initialize` method sets the pixel size of the object rectangle on the canvas (in this case it's a square with a width and height of 127 pixels). Also note the call to `self:repaint()` in the float handler for the inlet, which will redraw the graphical representation after updating the phase value. - -We still have to add the `dial:paint()` method to do all the actual drawing: - -~~~lua -function dial:paint(g) - local width, height = self:get_size() - local x, y = self:tip() - - -- standard object border, fill with bg color - g:set_color(0) - g:fill_all() - - -- dial face - g:fill_ellipse(2, 2, width - 4, height - 4) - g:set_color(1) - -- dial border - g:stroke_ellipse(2, 2, width - 4, height - 4, 4) - -- center point - g:fill_ellipse(width/2 - 3.5, height/2 - 3.5, 7, 7) - -- dial hand - g:draw_line(x, y, width/2, height/2, 2) -end -~~~ - -The existence of the `paint` method tells Pd-Lua that this is a graphical object. As mentioned before, this method receives a graphics context `g` as argument. The graphics context is an internal data structure keeping track of the graphics state of the object, which is used to invoke all the graphics operations. The `set_color` method of the graphics context is used to set the color for all drawing operations; in the case of `fill` operations it fills the graphical element with the color, while in the case of `stroke` operations it draws its border. There's just one color value, so we need to set the desired fill color in case of `fill`, and the desired stroke color in case of `stroke` operations. The color values 0 and 1 we use in this example are predefined, and indicate the default background color (usually white) and default foreground color (usually black), respectively. - -It is possible to choose other colors by calling `g:set_color(r, g, b)` with rgb color values instead, with each r, g, b value ranging from 0 to 255 (i.e., a byte value). For instance, the color "teal" would be specified as 0, 128, 128, the color "orange" as 255, 165, 0, "black" as 0, 0, 0, "white" as 255, 255, 255, etc. It's also possible to add a fourth *alpha* a.k.a. opacity value a, which is a floating point number in the range 0-1, where 0 means fully transparent, 1 fully opaque, and any value in between will blend in whatever is behind the graphical element to varying degrees. As of Pd-Lua 0.12.7, alpha values are fully supported in both plugdata and purr-data. In vanilla Pd they are simply ignored at present, so all graphical objects will be opaque no matter what alpha value you specify. - -Let's now take a closer look at the drawing operations themselves. We start out by filling the entire object rectangle, which is our drawing surface, with the default background color 0, using `g:fill_all()`. This operation is special in that it not only fills the entire object rectangle, but also creates a standard border rectangle around it. If you skip this, you'll get an object without border, which may be useful at times. - -We then go on to fill a circle with the background color, the dial's face. The graphics API has no operation to draw a circle, so we just draw an ellipse instead. The coordinates given to `g:fill_ellipse()` are the coordinates of the rectangle surrounding the ellipse. In this case the width and height values are what we specified with `self:set_size(127, 127)` in the `initialize` method, so they are identical, and thus our ellipse is in fact a circle. Also note that we make the ellipse a little smaller and put it at a small offset from the upper left corner, so the actual width and height are reduced by 4 and the shape is centered in the object rectangle (or square, in this case). - -Note that we could have skipped drawing the face entirely at this point, since it just draws a white circle on white background. But we could make the face a different color later, so it's good to include it anyway. - -After the face we draw its border, drawing the same ellipse again, but this time in the default foreground color and with a stroke width of 4. We then go on to draw the remaining parts, a small disk in the center which mimics the shaft on which the single hand of the dial is mounted, and the hand itself, which is just a simple line pointing in a certain direction. - -Which direction? I'm glad you asked. The line representing the hand goes from the center point width/2, height/2 to the point given by the x, y coordinates. Both width, height and x, y are calculated and assigned to local variables at the beginning of the `paint` method: - -~~~lua - local width, height = self:get_size() - local x, y = self:tip() -~~~ - -The `get_size()` call employs a built-in method which returns the current dimensions of the object rectangle; this is part of the graphics API. We could have used the constant 127 from the `initialize` method there, but we could change the size of the object rectangle later, so it's better not to hard-code the size in the `paint` method. - -The `tip()` method we have to define ourselves. It is supposed to calculate the coordinates of the tip of the hand. I have factored this out into its own routine right away, so that we can reuse it later when we add the mouse actions. Here it is: - -~~~lua -function dial:tip() - local width, height = self:get_size() - local x, y = math.sin(self.phase*math.pi), -math.cos(self.phase*math.pi) - x, y = (x/2*0.8+0.5)*width, (y/2*0.8+0.5)*height - return x, y -end -~~~ - -This basically just converts the position of the tip from polar coordinates (1, phase) to rectangular coordinates (x, y) and then translates and scales the normalized coordinates to pixel coordinates in the object rectangle which has its center at (width/2, height/2). We also put the tip at a normalized radius of 0.8 so that it is well within the face of the dial. Moreover, the formula computing the x, y pair accounts for the fact that the y coordinates of the object rectangle are upside-down (0 is at the top), and that we want the center-up (a.k.a. 12 o'clock) position to correspond to a zero phase angle. Hence the sin and cos terms have been swapped and the cos term adorned with a minus sign compared to the standard polar - rectangular conversion formula. - -So now that we hopefully understand all the bits and pieces, here's the Lua code of the object in its entirety again: - -~~~lua -local dial = pd.Class:new():register("dial") - -function dial:initialize(sel, atoms) - self.inlets = 1 - self.outlets = 0 - self.phase = 0 - self:set_size(127, 127) - return true -end - -function dial:in_1_float(x) - self.phase = x - self:repaint() -end - --- calculate the x, y position of the tip of the hand -function dial:tip() - local width, height = self:get_size() - local x, y = math.sin(self.phase*math.pi), -math.cos(self.phase*math.pi) - x, y = (x/2*0.8+0.5)*width, (y/2*0.8+0.5)*height - return x, y -end - -function dial:paint(g) - local width, height = self:get_size() - local x, y = self:tip() - - -- standard object border, fill with bg color - g:set_color(0) - g:fill_all() - - -- dial face - g:fill_ellipse(2, 2, width - 4, height - 4) - g:set_color(1) - -- dial border - g:stroke_ellipse(2, 2, width - 4, height - 4, 4) - -- center point - g:fill_ellipse(width/2 - 3.5, height/2 - 3.5, 7, 7) - -- dial hand - g:draw_line(x, y, width/2, height/2, 2) -end -~~~ - -#### Adding an outlet - -We can already send phase values into our dial object, but there's no way to get them out again. So let's add an outlet which lets us do that. Now that the grunt work is already done, this is rather straightforward. First we need to add the outlet in the `initialize` method: - -~~~lua - self.outlets = 1 -~~~ - -And then we just add a message handler for `bang` which outputs the value on the outlet: - -~~~lua -function dial:in_1_bang() - self:outlet(1, "float", {self.phase}) -end -~~~ - -Easy as pie. Here's how our patch looks like now: - -![Adding an outlet](18-graphics2.png) - -#### Mouse actions - -Our dial now has all the basic ingredients, but it still lacks one important piece: Interacting with the graphical representation itself using the mouse. The graphics API makes this reasonably easy since it provides us with four callback methods that we can implement. Each of these gets invoked with the current mouse coordinates relative to the object rectangle: - -- `mouse_down(x, y)`: called when the mouse is clicked -- `mouse_up(x, y)`: called when the mouse button is released -- `mouse_move(x, y)`: called when the mouse changes position while the mouse button is *not* pressed -- `mouse_drag(x, y)`: called when the mouse changes position while the mouse button *is* pressed - -Here we only need the `mouse_down` and `mouse_drag` methods which let us keep track of mouse drags in the object rectangle in order to update the phase value and recalculate the tip of the hand (I told you that the `tip()` method would come in handy again!). Here's the Lua code. Note that the `mouse_down` callback is used to initialize the `tip_x` and `tip_y` member variables, which we keep track of during the drag operation, so that we can detect in `mouse_drag` when it's time to output the phase value and repaint the object: - -~~~lua -function dial:mouse_down(x, y) - self.tip_x, self.tip_y = self:tip() -end - -function dial:mouse_drag(x, y) - local width, height = self:get_size() - - local x1, y1 = x/width-0.5, y/height-0.5 - -- calculate the normalized phase, shifted by 0.5, since we want zero to be - -- the center up position - local phase = math.atan(y1, x1)/math.pi + 0.5 - -- renormalize if we get an angle > 1, to account for the phase shift - if phase > 1 then - phase = phase - 2 - end - - self.phase = phase - - local tip_x, tip_y = self:tip(); - - if tip_x ~= self.tip_x or tip_y ~= self.tip_y then - self.tip_x = tip_x - self.tip_y = tip_y - self:in_1_bang() - self:repaint() - end -end -~~~ - -And here's the same patch again, which now lets us drag the hand to change the phase value: - -![Dial with mouse interaction](18-graphics3.png) - -#### 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: - -![A clock](18-graphics4.png) - -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: - -![A speedometer](18-graphics5.png) - -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: - -![Extended dial example](18-graphics6.png) - -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](#pdx.lua) section now. +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, in this section we are going to discuss 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 programming environments like Pd and Lua. Musicians also like to employ it to modify their algorithmic composition programs live on stage, which is where the term "live coding" comes from. You'll probably be using live coding a lot while developing your Pd-Lua externals, but I've kept this topic for the final section of this guide, because it requires a good understanding of Pd-Lua's basic features. So without any further ado, let's dive right into it 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. +First, we need to describe the predefined Pd-Lua object classes `pdlua` and `pdluax`, so that you know what's readily available. But we'll 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` method, which is also what both `pdlua` and `pdluax` use internally. ### pdlua @@ -1312,13 +822,13 @@ 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. +While `pdluax` is considered Pd-Lua's main workhorse for live coding, it also 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. +Fortunately, if you're not content with Pd-Lua's built-in 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. +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, you don't have to worry about these intricacies, but they *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 code doesn't break if we later change the directory structure. 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: @@ -1360,7 +870,7 @@ end Now launch the luatab.pd patch and connect a `reload` message to the `luatab wave` object, like so: -![Live-coding with dofilex 1](13-livecoding1.png) +![Livecoding example 1](13-livecoding1.png) Next change the wavetable function to whatever you want, e.g.: @@ -1372,19 +882,27 @@ 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: -![Live-coding with dofilex 2](14-livecoding2.png) +![Livecoding example 2](14-livecoding2.png) + +### Remote control + +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? And maybe even do that remotely from the editor, using the `pdsend` program? -### pdx.lua +Well, all this is in fact possible, but the implementation is a bit too involved to fully present it here. So we have provided this in a separate pdx.lua module, which you can find in the sources accompanying this tutorial. -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? +Setting up an object for this kind of remote control is easy, though. First, you need to import the `pdx` module into your script, using Lua's `require`: -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): +~~~lua +local pdx = require 'pdx' +~~~ + +Then just call `pdx.reload(self)` somewhere in the `initialize` method. This will set up the whole receiver/dofilex machinery in a fully automatic manner. Finally, add a message like this to your patch, which goes to the special `pdluax` receiver (note that this is completely unrelated to the `pdluax` object discussed previously, it just incidentally uses the same name): ~~~ ; pdluax reload ~~~ -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): +When clicked, this just reloads all Pd-Lua objects in the patch, provided they have been set up with `pdx.reload`. You can also specify the class to be reloaded (the receiver matches this against each object's class name): ~~~ ; pdluax reload foo @@ -1396,68 +914,86 @@ Or maybe name several classes, like so: ; pdluax reload foo, reload bar ~~~ -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: - -![Live-coding with remote control](15-remote-control1.png) - -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. +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 reload` message in a suitable way, e.g.: -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 1](15-remote-control1.png) -![Live-coding with pd-remote.pd](15-remote-control2.png) +You can then use `pdsend 4711 localhost udp` to transmit the `reload` message to Pd when needed. You probably don't want to run those commands yourself, but a 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 in the accompanying examples which shows how to do this. Once you've added this to your .emacs, you can just type Ctrl+C Ctrl+K in Emacs to make Pd reload your Lua script after saving it. It doesn't get much easier than that. Moreover, for your convenience I've added a little abstraction named pd-remote.pd which takes care of the `netreceive` and messaging bits and will look much tidier in your patches. --- -**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](https://melpa.org/), a large repository of Emacs packages. Please also check the [pd-remote](https://github.com/agraef/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. +**NOTE:** To use these facilities in your own patches, you'll have to copy pdx.lua and pd-remote.pd to your project directory or some other place where Pd finds them. The pd-remote.el file can be installed in your Emacs site-lisp directory if needed. Please also check the [pd-remote](https://github.com/agraef/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're not familiar with Emacs, or just prefer to use VS Code as an editor. --- -And here's a little gif showing the above patch in action. You may want to watch this in [Typora](https://www.typora.io/) or your favorite web browser to make the animation work. - -![Live-coding in action](16-remote-control2.gif) - -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. +So here's the full source code of our reworked `luatab` example (now with the `in_1_reload` handler removed and the `pdx.reload` call added to the `initialize` method): -### 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. +~~~lua +local luatab = pd.Class:new():register("luatab") -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. +-- our own pdlua extension, needed for the reload functionality +local pdx = require 'pdx' -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: +function luatab:initialize(sel, atoms) + -- single inlet for the frequency, bang goes to the single outlet when we + -- finished generating a new waveform + self.inlets = 1 + self.outlets = 1 + -- enable the reload callback + pdx.reload(self) + -- the name of the array/table should be in the 1st creation argument + if type(atoms[1]) == "string" then + self.tabname = atoms[1] + return true + else + self:error(string.format("luatab: expected array name, got %s", + tostring(atoms[1]))) + return false + end +end -~~~lua -function pdxtest:postreload() - -- stuff to do post-reload goes here - pd.post("Reloaded!") - -- instead of doing a full initialization, you could also just change the - -- number of inlets and outlets here - self:initialize() +function luatab:in_1_float(freq) + if type(freq) == "number" then + -- the waveform we want to compute, adjust this as needed + local function f(x) + return math.sin(2*math.pi*freq*(x+1))/(x+1) + end + -- get the Pd array and its length + local t = pd.Table:new():sync(self.tabname) + if t == nil then + self:error(string.format("luatab: array or table %s not found", + self.tabname)) + return + end + local l = t:length() + -- Pd array indices are zero-based + for i = 0, l-1 do + -- normalize arguments to the 0-1 range + t:set(i, f(i/l)) + end + -- this is needed to update the graph display + t:redraw() + -- output a bang to indicate that we've generated a new waveform + self:outlet(1, "bang", {}) + else + self:error(string.format("luatab: expected frequency, got %s", + tostring(freq))) + end end ~~~ -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](#signals) 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](17-live-xfade.png) - -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~`](#real-world-example-cross-fades) object from the Signals section. +And here's a little gif showing the above patch in action. You may want to watch this in [Typora](https://www.typora.io/) or your favorite web browser to make the animation work. -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. +![Remote control 2](16-remote-control2.gif) -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. +So there you have it: three (or rather four) different ways to live-code with Pd-Lua. Choose whatever best fits your purpose and is the most convenient for you. ## 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. +The examples accompanying this tutorial (including the pdx.lua, pdlua-remote.el and pdlua-remote.pd files mentioned at the end of the previous section) are also available for your perusal in the examples subdirectory of the folder where you found this document. + +But wait, there's more! As briefly mentioned in the introduction, the most recent Pd-Lua version also provides new facilities for audio signal processing and graphics. As these features are still fairly new, they are not yet covered in this tutorial. But you can find corresponding examples under pdlua/examples in the source or in the help browser. Specifically, check the sig-example folder for a simple example illustrating the use of signal inlets and outlets, and gui-example as well as osci3d for some GUI and graphics examples. -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 +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.