update maintainer email
[pdlua:pdlua.git] / src / pd.lua
1 --[[
2 pdlua -- a Lua embedding for Pd
3 Copyright (C) 2007,2008 Claude Heiland-Allen <claude@mathr.co.uk>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 --]]
19
20 -- storage for Pd C<->Lua interaction
21 pd._classes = { }
22 pd._objects = { }
23 pd._clocks = { }
24 pd._receives = { }
25
26 -- add a path to Lua's "require" search paths
27 pd._setrequirepath = function(path)
28   pd._packagepath = package.path
29   pd._packagecpath = package.cpath
30   if (pd._iswindows) then
31     package.path = path .. "\\?;" .. path .. "\\?.lua;" .. package.path
32     package.cpath = path .. "\\?.dll;" .. package.cpath
33   else
34     package.path = path .. "/?;" .. path .. "/?.lua;" .. package.path
35     package.cpath = path .. "/?.so;" .. package.cpath
36   end
37 end
38
39 -- reset Lua's "require" search paths
40 pd._clearrequirepath = function()
41   package.path = pd._packagepath
42   package.cpath = pd._packagecpath
43 end
44
45 -- constructor dispatcher
46 pd._constructor = function (name, atoms)
47   if nil ~= pd._classes[name] then
48     local o = pd._classes[name]:new():construct(name, atoms)
49     if o then
50       pd._objects[o._object] = o
51       return o._object
52     end
53   end
54   return nil
55 end
56
57 -- destructor dispatcher
58 pd._destructor = function (object)
59   if nil ~= pd._objects[object] then
60     pd._objects[object]:destruct()
61   end
62 end
63
64 -- inlet method dispatcher
65 pd._dispatcher = function (object, inlet, sel, atoms)
66   if nil ~= pd._objects[object] then
67     pd._objects[object]:dispatch(inlet, sel, atoms)
68   end
69 end
70
71 -- clock method dispatcher
72 pd._clockdispatch = function (c)
73   if nil ~= pd._clocks[c] then
74     local m = pd._clocks[c]._method
75     pd._clocks[c]._target[m](pd._clocks[c]._target)
76   end
77 end
78
79 -- prototypical OO system
80 pd.Prototype = { }
81 function pd.Prototype:new(o)
82   o = o or {}
83   setmetatable(o, self)
84   self.__index = self
85   return o
86 end
87
88
89 -- clocks
90 pd.Clock = pd.Prototype:new()
91 function pd.Clock:register(object, method)
92   if nil ~= object then
93     if nil ~= object._object then
94       self._clock = pd._createclock(object._object, method)
95       self._target = object
96       self._method = method
97       pd._clocks[self._clock] = self
98       return self
99     end
100   end
101   return nil
102 end
103 function pd.Clock:destruct()
104   pd._clocks[self._clock] = nil
105   pd._clockfree(self._clock)
106   self._clock = nil
107 end
108 function pd.Clock:dispatch()
109   local m = self._target[self._method]
110   if type(m) == "function" then
111     return m(self._target)
112   else
113     self._target:error(
114       "no method for `" .. self._method ..
115       "' at clock of Lua object `" .. self._name .. "'"
116     )
117   end
118 end
119 function pd.Clock:set(systime)
120   pd._clockset(self._clock, systime)
121 end
122 function pd.Clock:delay(delaytime)
123   pd._clockdelay(self._clock, delaytime)
124 end
125 function pd.Clock:unset()
126   pd._clockunset(self._clock)
127 end
128
129 -- tables
130 pd.Table = pd.Prototype:new()
131 function pd.Table:sync(name)
132   self.name = name
133   self._length, self._array = pd._getarray(name)
134   if self._length < 0 then
135     return nil
136   else
137     return self
138   end
139 end
140 function pd.Table:destruct()
141   self._length = -3
142   self._array = nil
143 end
144 function pd.Table:get(i)
145   if type(i) == "number" and 0 <= i and i < self._length then
146     return pd._readarray(self._length, self._array, i)
147   else
148     return nil
149   end
150 end
151 function pd.Table:set(i, f)
152   if type(i) == "number" and type(f) == "number" and 0 <= i and i < self._length then
153     return pd._writearray(self._length, self._array, i, f)
154   else
155     return nil
156   end
157 end
158 function pd.Table:length()
159   if self._length >= 0 then
160     return self._length
161   else
162     return nil
163   end
164 end
165 function pd.Table:redraw()
166   pd._redrawarray(self.name)
167 end
168
169 -- receivers
170 function pd._receivedispatch(receive, sel, atoms)
171   if nil ~= pd._receives[receive] then
172     pd._receives[receive]:dispatch(sel, atoms)
173   end
174 end
175 pd.Receive = pd.Prototype:new()
176 function pd.Receive:register(object, name, method)
177   if nil ~= object then
178     if nil ~= object._object then
179       self._receive = pd._createreceive(object._object, name)
180       self._name = name
181       self._target = object
182       self._method = method
183       pd._receives[self._receive] = self
184       return self
185     end
186   end
187   return nil
188 end
189 function pd.Receive:destruct()
190   pd._receives[self._receive] = nil
191   pd._receivefree(self._receive)
192   self._receive = nil
193   self._name = nil
194   self._target = nil
195   self._method = nil
196 end
197 function pd.Receive:dispatch(sel, atoms)
198   self._target[self._method](self._target, sel, atoms)
199 end
200
201 -- patchable objects
202 pd.Class = pd.Prototype:new()
203 function pd.Class:register(name)
204   if nil ~= pd._classes[name] then    -- already registered
205     return pd._classes[name]          -- return existing
206   else
207     self._class = pd._register(name)  -- register new class
208     pd._classes[name] = self          -- record registration
209     self._name = name
210     return self                       -- return new
211   end
212 end
213 function pd.Class:construct(sel, atoms)
214   self._object = pd._create(self._class)
215   self.inlets = 0
216   self.outlets = 0
217   if self:initialize(sel, atoms) then
218     pd._createinlets(self._object, self.inlets)
219     pd._createoutlets(self._object, self.outlets)
220     self:postinitialize()
221     return self
222   else
223     return nil
224   end
225 end
226 function pd.Class:destruct()
227   pd._objects[self] = nil
228   self:finalize()
229   pd._destroy(self._object)
230 end
231 function pd.Class:dispatch(inlet, sel, atoms)
232   local m = self["in_" .. inlet .. "_" .. sel]
233   if type(m) == "function" then
234     if sel == "bang"    then return m(self)           end
235     if sel == "float"   then return m(self, atoms[1]) end
236     if sel == "symbol"  then return m(self, atoms[1]) end
237     if sel == "pointer" then return m(self, atoms[1]) end
238     if sel == "list"    then return m(self, atoms)    end
239     return m(self, atoms)
240   end
241   m = self["in_n_" .. sel]
242   if type(m) == "function" then
243     if sel == "bang"    then return m(self, inlet)           end
244     if sel == "float"   then return m(self, inlet, atoms[1]) end
245     if sel == "symbol"  then return m(self, inlet, atoms[1]) end
246     if sel == "pointer" then return m(self, inlet, atoms[1]) end
247     if sel == "list"    then return m(self, inlet, atoms)    end
248     return m(self, inlet, atoms)
249   end
250   m = self["in_" .. inlet]
251   if type(m) == "function" then
252     return m(self, sel, atoms)
253   end
254   m = self["in_n"]
255   if type(m) == "function" then
256     return m(self, inlet, sel, atoms)
257   end
258   self:error(
259     "no method for `" .. sel ..
260     "' at inlet " .. inlet ..
261     " of Lua object `" .. self._name .. "'"
262   )
263 end
264 function pd.Class:outlet(outlet, sel, atoms)
265   pd._outlet(self._object, outlet, sel, atoms)
266 end
267 function pd.Class:initialize(sel, atoms) end
268 function pd.Class:postinitialize() end
269 function pd.Class:finalize() end
270 function pd.Class:dofile(file)
271   return pd._dofile(self._object, file)
272 end
273 function pd.Class:error(msg)
274   pd._error(self._object, msg)
275 end
276
277
278 local lua = pd.Class:new():register("lua")  -- global controls
279 function lua:initialize(sel, atoms)
280   self.inlets = 1
281   self.outlets = 0    -- FIXME: might be nice to have errors go here?
282   return true
283 end
284 function lua:in_1_load(atoms)  -- execute a script
285   self:dofile(atoms[1])
286 end
287
288
289 local luax = pd.Class:new():register("luax")  -- classless lua externals
290 function luax:initialize(sel, atoms)          -- motivation: pd-list 2007-09-23
291   local f = self:dofile(atoms[1] .. ".pd_luax")
292   if nil ~= f then
293     local atomstail = { }          -- munge for better lua<->luax compatibility
294     for i,_ in ipairs(atoms) do                  
295       if i > 1 then
296         atomstail[i-1] = atoms[i]
297       end
298     end
299     return f(self, atoms[1], atomstail)
300   else
301     return false   -- error message already output by dofile()
302   end
303 end