Package rats :: Module osc
[hide private]
[frames] | no frames]

Source Code for Module rats.osc

  1  #!/usr/bin/env python 
  2  # 
  3  # Toonloop for Python 
  4  # 
  5  # Copyright 2008 Alexandre Quessy & Tristan Matthews 
  6  # <alexandre@quessy.net> & <le.businessman@gmail.com> 
  7  # http://www.toonloop.com 
  8  # 
  9  # Original idea by Alexandre Quessy 
 10  # http://alexandre.quessy.net 
 11  # 
 12  # Toonloop is free software: you can redistribute it and/or modify 
 13  # it under the terms of the GNU General Public License as published by 
 14  # the Free Software Foundation, either version 3 of the License, or 
 15  # (at your option) any later version. 
 16  # 
 17  # Toonloop is distributed in the hope that it will be useful, 
 18  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 19  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 20  # GNU General Public License for more details. 
 21  # 
 22  # You should have received a copy of the gnu general public license 
 23  # along with Toonloop.  If not, see <http://www.gnu.org/licenses/>. 
 24  # 
 25  """ 
 26  Implementation of the OSC protocol for Twisted.  
 27   
 28  Server and clients use the pyliblo library to integrate well with a twisted  
 29  asynchronous networking application.  
 30   
 31  To use, install liblo from source (not from Ubuntu 8.10's package) and pyliblo.:: 
 32   
 33   mkdir -p ~/src 
 34   pushd ~/src 
 35   svn co https://liblo.svn.sourceforge.net/svnroot/liblo/trunk liblo  
 36   pushd liblo 
 37   ./autogen.sh 
 38   make 
 39   sudo make install 
 40   popd 
 41   wget http://das.nasophon.de/download/pyliblo-0.8.0.tar.gz 
 42   tar -zxvf pyliblo-0.8.0.tar.gz 
 43   pushd pyliblo-0.8.0/ 
 44   python setup.py  build 
 45   sudo python setup.py install --prefix=/usr/local 
 46   popd 
 47   popd 
 48  """ 
 49  import liblo 
 50  import sys 
 51  import time 
 52   
 53  from twisted.internet import reactor  
 54   
 55  VERBOSE = True 
 56   
57 -class OscServerError(Exception):
58 """ 59 Raised when any OSC server error happens. 60 """ 61 pass
62
63 -class OscClientError(Exception):
64 """ 65 Raised when any OSC client error happens. 66 """ 67 pass
68
69 -class OscClient(object):
70 """ 71 An OSC client can send OSC messages. 72 :param args: one or two arguments. 73 If one, it is either the port number or url. 74 If two, it is the host and the port. 75 [host], [port] 76 [addr | port] 77 """
78 - def __init__(self, *args):
79 self.args = args 80 if len(args) == 0: 81 args = 1234 # default 82 self._target = None 83 self._setup()
84
85 - def _setup(self):
86 try: 87 self._target = liblo.Address(*self.args) 88 except liblo.AddressError, e: 89 raise # OscClientError(str(e)) # TODO: what about the traceback?
90
91 - def set_args(self, *args):
92 """ 93 Changes the port|host to send to. 94 """ 95 #if port == self.port: 96 # print "Port is already %d" % (port) 97 #else: 98 self.args = args 99 self._setup()
100
101 - def send_message(self, addr, *args):
102 """ 103 See liblo.send : Simply omit the first argument. 104 105 :param addr: OSC address such as /foo/bar 106 Other args are arguments types and values. 107 108 You can use tuple of (type, value) 109 Types are "i", "s", "f", etc. 110 """ 111 liblo.send(self._target, addr, *args)
112
113 -class OscServer(object):
114 """ 115 An OSC server listens for incoming OSC messages. 116 117 The programmer must register callbacks in order to do something with the incoming data. 118 """
119 - def __init__(self, port=1234, interval=0.01):
120 """ 121 :param interval: duration in seconds to wait between each poll 122 """ 123 self.port = port 124 self._callbacks = [] # list of lists 125 self.interval = interval 126 self._server = None 127 self._delayed_id = None 128 #TODO: self._looping_call = None 129 self.started = False
130
131 - def start(self):
132 """ 133 Actually start the server. 134 """ 135 self._setup()
136
137 - def set_port(self, port):
138 """ 139 Used to change the OSC port to listen to. 140 """ 141 if port == self.port: 142 print "Port is already %d" % (port) 143 else: 144 self.port = port 145 if self.started: 146 self._setup()
147
148 - def add_callback(self, addr, types, callback, *args):
149 """ 150 Adds a callback for an OSC message. 151 152 :param addr: URL-like OSC address/pattern 153 :param type: string that enumerates the OSC types the method accepts 154 :param callback: pointer to a python function or method 155 :param args: List of any number of user data. 156 157 What liblo does is to check for matching callbacks in the order they 158 were registered, and when a match is found, the callback function is 159 called immediately. This means that, if the fallback is to be called 160 only when nothing else matches, it needs to be registered last. 161 """ 162 global VERBOSE 163 for i in range(len(self._callbacks)): 164 if self._callbacks[i][0] == addr: 165 if VERBOSE: 166 print("Overriding the previous %s callback." % (addr)) 167 self._callbacks[i] = [addr, types, callbacks, args] 168 return 169 # else 170 self._callbacks.append([addr, types, callback, args])
171
172 - def _setup(self):
173 """ Called once it is time to start the server or change the port to listen to. """ 174 global VERBOSE 175 if VERBOSE: 176 print("STarting OSC server on port %d" % (self.port)) 177 print("If you experience a long delay while the application is frozen, you might need to upgrade you liblo to the latest version instead of the version packaged with your operating system. Use the latest tarball instead of the .deb.") 178 try: 179 self._server = liblo.Server(self.port) 180 except liblo.ServerError, e: 181 raise # OscServerError(str(e)) # TODO: what about the traceback? 182 else: 183 for callback_list in self._callbacks: 184 addr, types, callback, args = callback_list 185 if VERBOSE: 186 print("Adding OSC method %s %s %s %s" % (addr, types, callback, args)) 187 self._server.add_method(addr, types, callback, *args) 188 self.started = True 189 self._delayed_id = reactor.callLater(self.interval, self._poll)
190
191 - def _poll(self):
192 """ Called often by twisted.reactor to poll the OSC server. """ 193 self._server.recv(0) 194 if self.started: 195 # TODO: check if self._delayed_id is not None 196 self._delayed_id = reactor.callLater(self.interval, self._poll)
197 198 if __name__ == "__main__": 199 # examples :
200 - def foo_bar_callback(path, args):
201 i, f = args 202 print "received message '%s' with arguments '%d' and '%f'" % (path, i, f)
203
204 - def set_port_callback(path, args, types, src, data):
205 i = args[0] 206 print "received message '%s' with argument '%d' " % (path, i) 207 print "user data : ", data 208 data.set_port(i)
209
210 - def foo_baz_callback(path, args, types, src, data):
211 print "received message '%s'" % path 212 print "blob contains %d bytes, user data was '%s'" % (len(args[0]), data)
213
214 - def fallback(path, args, types, src):
215 print "got unknown message '%s' from '%s'" % (path, src.get_url()) 216 for a, t in zip(args, types): 217 print "argument of type '%s': %s" % (t, a)
218
219 - def ping_callback(path, args):
220 i, f, s = args 221 print "received message '%s' with arguments '%d', '%f' and '%s'" % (path, i, f, s)
222
223 - def send_something(client_addr):
224 c = OscClient(client_addr) 225 c.send_message("/ping", ('i', 123), ('f', 3.13159), ('s', "hello"))
226 227 # UDP broadcasting 228 # could be to 255.255.255.255 or 192.168.0.255 or so. 229 client_addr = "osc.udp://127.0.0.1:1234" 230 # client_addr = "osc.udp://192.168.1.255:1234" 231 # client_addr = "osc.udp://255.255.255.255:1234" 232 # client_addr = "osc.udp://74.57.167.255:1234" 233 s = OscServer(1234) 234 s.add_callback("/foo/bar", "if", foo_bar_callback) 235 s.add_callback("/set/port", "i", set_port_callback, s) # passing the server itself as user data 236 s.add_callback("/foo/baz", "b", foo_baz_callback, "blah") 237 s.add_callback("/ping", "ifs", ping_callback) 238 s.add_callback(None, None, fallback) 239 print("Will now start listening OSC server...") 240 s.start() # this hangs on Ubuntu 8.10. Update liblo-dev to latest tarball. 241 print("Server started. ") 242 reactor.callLater(1.0, send_something, client_addr) 243 244 try: 245 reactor.run() 246 except KeyboardInterrupt: 247 print("Exiting.") 248