1
2 """
3 Communication with Pure Data.
4 start also the pd patch.
5 """
6 import pprint
7 import sys
8
9 from twisted.internet import reactor
10 from twisted.internet.error import CannotListenError
11
12 from rats import fudi
13
15 """
16 Toonloop API for Pure Data FUDI network messages.
17
18 /frame/add
19 /frame/remove
20 /clip/select <int>
21 /ping
22 /pong
23
24 Restricted / dangerous ones :
25
26 /config/set <string> <any>
27 /call <string> ...
28 /quit
29 """
31 self.receive_port = 15555
32 self.send_port = 17777
33 self.send_host = "localhost"
34 self.receiver = None
35 self.sender = None
36 self.verbose = True
37 self.app = None
38 self.try_again_delay = 30
39 self.try_again = True
40 self.manhole_enable = True
41 self.__dict__.update(**kwargs)
42 self._init_receiver()
43 self._init_sender()
44
46 if self.verbose:
47 print("starting FUDI server on port" + str(self.receive_port))
48 self.receiver = fudi.FUDIServerFactory()
49 self.receiver.register_message("/ping", self.ping)
50 self.receiver.register_message("/pong", self.pong)
51 self.receiver.register_message("/frame/add", self.frame_add)
52 self.receiver.register_message("/frame/remove", self.frame_remove)
53 self.receiver.register_message("/clip/select", self.frame_remove)
54 self.receiver.register_message("/option/group", self.set_option_in_group)
55 if self.manhole_enable:
56 self.receiver.register_message("/call", self.call)
57 self.receiver.register_message("/config/set", self.config_set)
58 self.receiver.register_message("/quit", self.quit)
59 try:
60 reactor.listenTCP(self.receive_port, self.receiver)
61 except CannotListenError, e:
62 print e.message
63 raise
64 self.receiver = None
65 return self.receiver
66
72
74 if self.verbose:
75 print "pd: Connected !"
76 print "pd: sender:", sender
77 print "pd: receiver:", self.receiver
78 self.sender = sender
79 self.sender.send_message("/ping", 1, 2.0, "bang")
80 if self.verbose:
81 print " sent ping"
82
84 print 'FUDI server unavailable:', self.send_host, self.send_port
85
86 if self.try_again:
87 reactor.callLater(self.try_again_delay, self._init_sender)
88
90
91 if self.sender is None:
92 print "pd: Cannot send. Sender is None"
93 else:
94 try:
95 self.sender.send_message(selector, *args)
96 except Exception, e:
97 print e.message
98
99 - def ping(self, receiver, *args):
100 """
101 Answers /pong to /ping
102 /ping
103 """
104 if self.verbose:
105 print "pd: received ping", args
106 response = list(args)
107 if self.verbose:
108 print "pd: will send", "pong", response
109 self._send_message("pong", *response)
110
111 - def pong(self, receiver, *args):
112 """
113 Receives Pong.
114 /pong
115 """
116 if self.verbose:
117 print "pd: received pong", args
118
120 """
121 Sets an configuration option.
122 /options/set <name> <value>
123 """
124 if self.verbose:
125 print "pd: config_set", args
126 if self.app is not None:
127 try:
128 k = args[0]
129 v = args[1]
130 kind = self.app.config.set(k, v)
131 print " set %s = %s (%s)" % (k, k, kind)
132 except Exception, e:
133 print e.message
134
136 """
137 Grabs a frame.
138 /frame/add
139 """
140 if self.verbose:
141 print "pd: /frame/add", args
142 if self.app is not None:
143 try:
144 self.app.frame_add()
145 except Exception, e:
146 print e.message
147
149 if self.verbose:
150 print "pd: /option/group", args
151 if self.app is not None:
152 try:
153 group = args[0]
154 key = args[1]
155 value = args[2]
156 if len(args) == 5:
157 value = "%s,%s,%s" % (args[2], args[3], args[4])
158 self.app.set_option_in_group(group, key, value)
159 except IndexError, e:
160 print("IndexError: %s" % (e.message))
161 except Exception, e:
162 print("Error setting option from group %s" % (e.message))
163
165 """
166 removes a frame.
167 /frame/remove
168 """
169 if self.verbose:
170 print "pd: /frame/remove", args
171 if self.app is not None:
172 try:
173 self.app.frame_remove()
174 except Exception, e:
175 print e.message
176
178 """
179 Select a clip by number.
180
181 /clip select <index>
182 index=0
183 """
184 if self.verbose:
185 print "pd: /clip/select", args
186 if self.app is not None:
187 try:
188 self.app.clip_select(int(args[0]))
189 except Exception, e:
190 print e.message
191
192 - def call(self, receiver, *args):
193 """
194 Wraps any method from the APP.
195
196 Warning : protect this port !
197 Do not use this FUDI protocol when network is not secure.
198
199 The pound sign (#) is replaced by commas
200
201 call <method> <args...>
202 """
203 if self.app is None:
204 print 'ERROR: puredata.app is None'
205 else:
206 try:
207 method = getattr(self.app, args[0])
208 actual_args = list(args[1:])
209
210
211 if self.verbose:
212 print ' puredata.%s(%s)' % (args[0], str(actual_args)), method
213 method(*args[1:])
214 except Exception, e:
215 print "ERROR @ puredata.call:", e.message
216 print sys.exc_info()
217
218 - def quit(self, receiver, *args):
219 """
220 Quits the application.
221
222 Warning : protect this port !
223 Do not use this FUDI protocol when network is not secure.
224
225 /quit
226 """
227 if self.verbose:
228 print "pd: received quit", args
229 if self.app is not None:
230 self.app.quit()
231 else:
232 reactor.stop()
233
235 """
236 Factory for the Toonloop FUDI service.
237
238 **kwargs
239 """
240 fudi.VERBOSE = True
241 print("Starting PureData service.")
242 pprint.pprint(kwargs)
243 pd = PureDataController(**kwargs)
244 return pd
245
246 if __name__ == '__main__':
247 fudi.VERBOSE = True
248 pd = PureDataController()
249 reactor.callLater(15, pd._init_sender)
250 reactor.run()
251