From 267170167d52cab9e97f879d9127a1cf04f6bb58 Mon Sep 17 00:00:00 2001 From: Martin Peach Date: Tue, 15 Mar 2011 20:53:57 +0000 Subject: This is a version of Claude Heiland-Allen's lua for Pd. The objects are named pdlua and pdluax instead of lua and luax. So far it seems to work on linux. svn path=/trunk/externals/pdlua/; revision=15030 --- doc/Makefile | 5 ++ doc/internal.txt | 77 +++++++++++++++++++++++++++ doc/lua.txt | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/luax.txt | 71 +++++++++++++++++++++++++ doc/pdlua.tex | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 457 insertions(+) create mode 100644 doc/Makefile create mode 100644 doc/internal.txt create mode 100644 doc/lua.txt create mode 100644 doc/luax.txt create mode 100644 doc/pdlua.tex (limited to 'doc') diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..9b0f913 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,5 @@ +all: pdlua.pdf + +%.pdf: %.tex + pdflatex $< + pdflatex $< diff --git a/doc/internal.txt b/doc/internal.txt new file mode 100644 index 0000000..92e4c37 --- /dev/null +++ b/doc/internal.txt @@ -0,0 +1,77 @@ +Internal Stuff +============== + +This is a developer document, not a user document. You probably don't +need to read this. + + +Private/Internal Variables +-------------------------- + +Everything that the user shouldn't dabble with starts with '_'. +For example 'pd._constructor', 'self._object', etc. This includes +everything that messes with pointers, for many purposes have a +stub: function f(self, ...) return _f(self._object, ...) end + +Things that the user can use shouldn't start with '_'. + +Everything in pdlua should be in the 'pd' global table. + + +Table Indices +------------- + +As per Lua conventions, Lua tables start at index 1. See: + +lua.c/pdlua_pushatomtable() (C->Lua) +lua.c/pdlua_popatomtable() (Lua->C) (makes additional assumptions) + + +Inlet/Outlet Numbers +-------------------- + +C code uses 0,1,... +Lua code uses 1,2,... + +Translations are performed in: + +lua.c/pdlua_dispatch() (C->Lua) +lua.c/pdlua_outlet() (Lua->C) + + +Pointers +-------- + +Pointers are all Light User Data values. This means there is no type +safety, make sure you're using the right pointers! + +pd._classes :: string => Lua object (class prototypes) +object._class :: t_class* + +pd._objects :: t_pdlua* => Lua object (object instances) +object._object :: t_pdlua* + +pd._clocks :: t_clock* => Lua object (clock instances) +clock._clock :: t_clock* + +pdtable._array :: PDLUA_ARRAY_ELEM* + +Pointer atoms are also stored as Light User Data. It's possible for +things to crash if they are used/stored/etc, as far as I understand the +way Pd uses them. + + +Architecture Issues +------------------- + +:initialize() is called before the object is created. +:postinitialize() is called after the object is created. + + +Other Issues +------------ + +"#t does not invoke the __len metamethod when t is a table." +See end of: http://lua-users.org/wiki/GeneralizedPairsAndIpairs + +This means pd.Table will remain ugly, with :length() :set() :get() diff --git a/doc/lua.txt b/doc/lua.txt new file mode 100644 index 0000000..486380a --- /dev/null +++ b/doc/lua.txt @@ -0,0 +1,148 @@ +pdlua +=== + +The Lua loader included in -lib pdlua allows externals for Pd to be +written in the Lua programming language. + +If you try to create an object [foo] in Pd, Pd checks if the class +"foo" exists. If it doesn't, it tries to load an external file that +"probably" will contain code for "foo". The Lua loader adds support +for loading "foo.pd_lua" when you try to create [foo]. + + +Class Creation +-------------- + +The first expression/statement in the file should be of the form: + + local foo = pd.Class:new():register("foo") + +This creates a new Pd class called "foo". The 'local' declaration +is optional, but recommended -- without it, 'foo' is global, which +means any Lua code can modify it (possibly by accident). + + +Object Initialization +--------------------- + +Then you can add methods to the Pd class. The most important one +is 'initialize', which is executed when a new object is created. + + function foo:initialize(sel, atoms) + -- code + end + +or equivalently: + + foo.initialize = function (self, sel, atoms) + -- code + end + +'sel' is usually (always?) the class name, 'atoms' are the creation +arguments in a Lua table. An example: + + Pd :: [foo a b 1 2 3 c] + sel == "foo" + atoms == { "a", "b", 1, 2, 3, "c" } + +Being a method, 'initialize' has a 'self' variable (which is the +object to be created), and if you want your objects to have inlets +or outlets you need need to set those fields in this method (Pd +doesn't support changing the number of inlets or outlets after an +object is created): + + self.inlets = 1 + self.outlets = atoms[1] + +The default inlet/outlet counts are 0. + +The return value of 'initialize' is used to allow objects to fail +to create (for example, if the creation arguments are bad). Most +of the time you will 'return true', but if you really can't create +then you can 'return false'. + +If you need to do things after the Pd object is created, but before +control is returned to Pd, you can use the 'postinitialize' method: + + function foo:postinitialize() + -- code + end + + +Object Finalization +------------------- + +The 'finalize' method is called when the object is deleted by Pd. +You can clean up stuff here if needed. The default implementation +does nothing. + + +Inlet Methods +------------- + +FIXME: write about inlet methods/dispatching +FIXME: for now, see examples/*.pd_lua and src/pd.lua + + +Sending To Outlets +------------------ + +FIXME: write about self:outlet(outletNumber, selector, atoms) +FIXME: for now, see examples/*.pd_lua and src/pd.lua + + +Sending To Receivers +-------------------- + +You can send messages to receivers like this: + + pd.send("receiver", "selector", { "a", "message", 1, 2, 3 } + +See examples/lsend.pd_lua for details. + + +Receivers +--------- + +You can bind methods to receivers, to get messages from +[send receiver] and "; receiver message". + +See examples/lreceive.pd_lua for details. + +Remember to clean up your receivers in object:finalize(), or weird +things will happen. + + +Clocks +------ + +You can bind methods to clocks, for timing based on Pd's logical clock. + +See examples/ldelay.pd_lua for details. + +Remember to clean up your clocks in object:finalize(), or weird things +will happen. + + +Miscellaneous Object Methods +---------------------------- + +Execute a Lua file using Pd's path to find it: + + self:dofile("filename") + +Report an error to Pd's console: + + self:error("message") + + +Miscellaneous Functions +----------------------- + +Print a string to Pd's console: + + pd.post("a string") + +Note that pd.post() should not really be used for errors. + +FIXME: add pd.error() for error messages diff --git a/doc/luax.txt b/doc/luax.txt new file mode 100644 index 0000000..dbaadb4 --- /dev/null +++ b/doc/luax.txt @@ -0,0 +1,71 @@ +pdluax +==== + +The pdluax class allows "volatile" loading of Lua source code files +that define Pd object behaviour. + +The [pdluax foo] object loads "foo.pd_luax" at object creation time. + + +Advantages +---------- + ++ You can edit "foo.pd_luax" and new [pdluax foo] objects will reflect + the changes in the file. + ++ Good for rapid development/testing cycles. + ++ Good for live coding. + ++ No need to restart Pd if you made a little mistake. + + +Disadvantages +------------- + +- Reloading the file each time is slower. + +- Syntax is different to the syntax expected by the Lua loader + (see below for discussion). + +- There is no "reload" functionality, so you can have multiple + objects called [pdluax foo] but that have different behaviours. + +- Data shared between objects must be accessible globally. + +- The above two points mean some mistakes/changes mean you have + to restart Pd anyway. + + +How To Write Code For pdluax +-------------------------- + +The last expression/statement in the file should be of the form: + + return function (self, sel, atoms) + -- code here + end + +This function is executed in the context of the 'initialize' +method of the pdluax class, and has the same arguments: + + 'self' is the object to be created. + 'sel' is the name of the class. + 'atoms' are the creation arguments. + +To add methods to the new object you need to add code inside +the returned function. There are two syntaxes for this: + + function self:in_1_float(f) ... end + +or + + self.in_1_float = function(self, f) ... end + +If using the second form, remember the self argument. + +If you need a shared state between objects, you need to use a +global name. Try to pick something unique to avoid conflicts +with other scripts. You also need to ensure that you don't +clobber this state - remember the script can be executed more +than once. diff --git a/doc/pdlua.tex b/doc/pdlua.tex new file mode 100644 index 0000000..becceb9 --- /dev/null +++ b/doc/pdlua.tex @@ -0,0 +1,156 @@ +\documentclass{article} + +\title{pdlua} +\author{Claude Heiland-Allen +\\ +\tt{claudiusmaximus@goto10.org}} + +\begin{document} + +\maketitle + +\begin{abstract} +Pd (aka Pure-data) is a real-time visual programming environment +primarily used for multimedia processing. Lua is a powerful, fast, +light-weight, embeddable scripting language. pdlua is a Lua embedding +for Pd. +\end{abstract} + +\section{Classes and objects} + +\begin{verbatim} +-- example1.pd_lua +local example1 = pd.Class:new():register("example1") +function example1:initialize(sel, atoms) + return true +end +\end{verbatim} + +{\tt pd} is a package automatically available to scripts loaded by pdlua. +pdlua uses a prototype-based object system and {\tt pd.Class} is the +prototype for classes that define patchable objects in Pd. To create a +new class, use the {\tt :new()} method. This creates an anonymous class, +which needs to be registered with Pd using the {\tt :register()} method. + +The new class {\tt example1} cannot be instantiated yet, as the default +{\tt :initialize()} method returns {\tt false}, indicating to pdlua that +the Pd object should not be created. + +With the code above, {\tt example1} can be created in Pd, but it will +have neither inlets nor outlets. + +\section{Inlets and methods} + +\begin{verbatim} +-- example2.pd_lua +local example2 = pd.Class:new():register("example2") +function example2:initialize(sel, atoms) + self.inlets = 3 + return true +end +\end{verbatim} + +Setting {\tt self.inlets} in the {\tt :initialize()} method will give +the created objects some inlets, in this case three of them. Not very +interesting yet, as sending messages to these inlets will result in +errors as there are no methods to respond to messages at these inlets. + +Messages arriving at the Pd object's inlets are dispatched to the Lua +object's {\tt :in\_*()} methods. There are five predefined selectors: + +\begin{itemize} +\item {\tt bang} +\item {\tt float} +\item {\tt symbol} +\item {\tt pointer} +\item {\tt list} +\end{itemize} + +They can be used like this: + +\begin{verbatim} +function example2:in_1_bang() + pd.post("inlet 1 got a bang") +end +function example2:in_1_float(f) + pd.post("inlet 1 got a float: " .. f) +end +function example2:in_1_symbol(s) + pd.post("inlet 1 got a symbol: " .. s) +end +function example2:in_1_pointer(p) + pd.post("inlet 1 got a pointer) +end +function example2:in_1_list(atoms) + pd.post("inlet 1 got a list: " .. #atoms .. " elements") +end +\end{verbatim} + +In the above, the methods are defined for the leftmost inlet. To add +methods for the other inlets, replace {\tt :in\_1\_*()} with +{\tt :in\_2\_*()} for the second inlet, or {\tt :in\_3\_*()} for the third, +and so on. + +It is possible to add methods for other selectors: + +\begin{verbatim} +function example2:in_2_baabaa(atoms) + pd.post("inlet 2 got a baabaa: " .. #atoms .. " elements") +end +function example2:in_2_moomoo(atoms) + pd.post("inlet 2 got a moomoo: " .. #atoms .. " elements") +end +\end{verbatim} + +It is also possible to add methods that catch any selector: + +\begin{verbatim} +function example2:in_3(sel, atoms) + pd.post("inlet 3 got a " .. sel .. ": .. #atoms .. " elements") +end +\end{verbatim} + +Or methods that catch messages at any inlet: + +\begin{verbatim} +function example2:in_n_float(i, f) + pd.post("inlet " .. i .. " got a float: " .. f) +end +function example2:in_n_quack(i, atoms) + pd.post("inlet " .. i .. " got a quack: " .. #atoms .. " elements") +end +\end{verbatim} + +Or even catch any message at any inlet: + +\begin{verbatim} +function example2:in_n(i, sel, atoms) + pd.post("inlet " .. i .. " got a " .. sel .. ": " .. #atoms .. " elements") +end +\end{verbatim} + +The more specific methods are called before the more general methods: + +\begin{itemize} +\item {\tt :in\_1\_selector()} +\item {\tt :in\_n\_selector()} +\item {\tt :in\_1()} +\item {\tt :in\_n()} +\item {\tt :error("no method found")} +\end{itemize} + +\section{Outlets} + +\section{Sends} + +\section{Receives} + +\section{Values} + +\section{Tables} + +\section{Clocks} + +\section{Paths} + +\end{document} -- cgit v1.2.1