1# 2# pyliblo - Python bindings for the liblo OSC library 3# 4# Copyright (C) 2007-2015 Dominic Sacré <dominic.sacre@gmx.de> 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU Lesser General Public License as 8# published by the Free Software Foundation; either version 2.1 of the 9# License, or (at your option) any later version. 10# 11 12__version__ = '0.10.0' 13 14 15from cpython cimport PY_VERSION_HEX 16cdef extern from 'Python.h': 17 void PyEval_InitThreads() 18 19from libc.stdlib cimport malloc, free 20from libc.math cimport modf 21from libc.stdint cimport int32_t, int64_t 22 23from liblo cimport * 24 25import inspect as _inspect 26import weakref as _weakref 27 28 29class _weakref_method: 30 """ 31 Weak reference to a function, including support for bound methods. 32 """ 33 __slots__ = ('_func', 'obj') 34 35 def __init__(self, f): 36 if _inspect.ismethod(f): 37 if PY_VERSION_HEX >= 0x03000000: 38 self._func = f.__func__ 39 self.obj = _weakref.ref(f.__self__) 40 else: 41 self._func = f.im_func 42 self.obj = _weakref.ref(f.im_self) 43 else: 44 self._func = f 45 self.obj = None 46 47 @property 48 def func(self): 49 if self.obj: 50 return self._func.__get__(self.obj(), self.obj().__class__) 51 else: 52 return self._func 53 54 def __call__(self, *args, **kwargs): 55 return self.func(*args, **kwargs) 56 57 58class struct: 59 def __init__(self, **kwargs): 60 for k, v in kwargs.items(): 61 setattr(self, k, v) 62 63 64cdef str _decode(s): 65 # convert to standard string type, depending on python version 66 if PY_VERSION_HEX >= 0x03000000 and isinstance(s, bytes): 67 return s.decode() 68 else: 69 return s 70 71cdef bytes _encode(s): 72 # convert unicode to bytestring 73 if isinstance(s, unicode): 74 return s.encode() 75 else: 76 return s 77 78 79# forward declarations 80cdef class _ServerBase 81cdef class Address 82cdef class Message 83cdef class Bundle 84 85 86# liblo protocol constants 87UDP = LO_UDP 88TCP = LO_TCP 89UNIX = LO_UNIX 90 91 92################################################################################ 93# timetag 94################################################################################ 95 96cdef lo_timetag _double_to_timetag(double f): 97 cdef lo_timetag tt 98 cdef double intr, frac 99 frac = modf(f, &intr) 100 tt.sec = <uint32_t>intr 101 tt.frac = <uint32_t>(frac * 4294967296.0) 102 return tt 103 104cdef double _timetag_to_double(lo_timetag tt): 105 return <double>tt.sec + (<double>(tt.frac) / 4294967296.0) 106 107def time(): 108 """ 109 Return the current time as a floating point number (seconds since 110 January 1, 1900). 111 """ 112 cdef lo_timetag tt 113 lo_timetag_now(&tt) 114 return _timetag_to_double(tt) 115 116 117################################################################################ 118# send 119################################################################################ 120 121cdef _send(target, _ServerBase src, args): 122 cdef lo_server from_server 123 cdef Address target_address 124 cdef int r 125 126 # convert target to Address object, if necessary 127 if isinstance(target, Address): 128 target_address = target 129 elif isinstance(target, tuple): 130 # unpack tuple 131 target_address = Address(*target) 132 else: 133 target_address = Address(target) 134 135 # 'from' parameter is NULL if no server was specified 136 from_server = src._server if src else NULL 137 138 if isinstance(args[0], (Message, Bundle)): 139 # args is already a list of Messages/Bundles 140 packets = args 141 else: 142 # make a single Message from all arguments 143 packets = [Message(*args)] 144 145 # send all packets 146 for p in packets: 147 if isinstance(p, Message): 148 message = <Message> p 149 r = lo_send_message_from(target_address._address, 150 from_server, 151 message._path, 152 message._message) 153 else: 154 bundle = <Bundle> p 155 r = lo_send_bundle_from(target_address._address, 156 from_server, 157 bundle._bundle) 158 159 if r == -1: 160 raise IOError("sending failed: %s" % 161 <char*>lo_address_errstr(target_address._address)) 162 163 164def send(target, *args): 165 """ 166 send(target, *messages) 167 send(target, path, *args) 168 169 Send messages to the the given target, without requiring a server. 170 Arguments may be one or more :class:`Message` or :class:`Bundle` objects, 171 or a single message given by its path and optional arguments. 172 173 :param target: 174 the address to send the message to; an :class:`Address` object, 175 a port number, a ``(hostname, port)`` tuple, or a URL. 176 :param messages: 177 one or more objects of type :class:`Message` or :class:`Bundle`. 178 :param path: 179 the path of the message to be sent. 180 181 :raises AddressError: 182 if the given target is invalid. 183 :raises IOError: 184 if the message couldn't be sent. 185 """ 186 _send(target, None, args) 187 188 189################################################################################ 190# Server 191################################################################################ 192 193class ServerError(Exception): 194 """ 195 Raised when creating a liblo OSC server fails. 196 """ 197 def __init__(self, num, msg, where): 198 self.num = num 199 self.msg = msg 200 self.where = where 201 def __str__(self): 202 s = "server error %d" % self.num 203 if self.where: s += " in %s" % self.where 204 s += ": %s" % self.msg 205 return s 206 207 208cdef int _msg_callback(const_char *path, const_char *types, lo_arg **argv, 209 int argc, lo_message msg, void *cb_data) with gil: 210 cdef int i 211 cdef char t 212 cdef unsigned char *ptr 213 cdef uint32_t size, j 214 215 args = [] 216 217 for i from 0 <= i < argc: 218 t = types[i] 219 if t == 'i': v = argv[i].i 220 elif t == 'h': v = argv[i].h 221 elif t == 'f': v = argv[i].f 222 elif t == 'd': v = argv[i].d 223 elif t == 'c': v = chr(argv[i].c) 224 elif t == 's': v = _decode(&argv[i].s) 225 elif t == 'S': v = _decode(&argv[i].s) 226 elif t == 'T': v = True 227 elif t == 'F': v = False 228 elif t == 'N': v = None 229 elif t == 'I': v = float('inf') 230 elif t == 'm': v = (argv[i].m[0], argv[i].m[1], argv[i].m[2], argv[i].m[3]) 231 elif t == 't': v = _timetag_to_double(argv[i].t) 232 elif t == 'b': 233 if PY_VERSION_HEX >= 0x03000000: 234 v = bytes(<unsigned char*>lo_blob_dataptr(argv[i])) 235 else: 236 # convert binary data to python list 237 v = [] 238 ptr = <unsigned char*>lo_blob_dataptr(argv[i]) 239 size = lo_blob_datasize(argv[i]) 240 for j from 0 <= j < size: 241 v.append(ptr[j]) 242 else: 243 v = None # unhandled data type 244 245 args.append(v) 246 247 cdef char *url = lo_address_get_url(lo_message_get_source(msg)) 248 src = Address(url) 249 free(url) 250 251 cb = <object>cb_data 252 func = cb.func.func 253 254 func_args = (_decode(<char*>path), 255 args, 256 _decode(<char*>types), 257 src, 258 cb.user_data) 259 260 # call function 261 if _inspect.getargspec(func)[1] == None: 262 # determine number of arguments to call the function with 263 n = len(_inspect.getargspec(func)[0]) 264 if _inspect.ismethod(func): 265 n -= 1 # self doesn't count 266 r = cb.func(*func_args[0:n]) 267 else: 268 # function has argument list, pass all arguments 269 r = cb.func(*func_args) 270 271 return r if r != None else 0 272 273 274cdef int _bundle_start_callback(lo_timetag t, void *cb_data) with gil: 275 cb = <object>cb_data 276 r = cb.start_func(_timetag_to_double(t), cb.user_data) 277 return r if r != None else 0 278 279 280cdef int _bundle_end_callback(void *cb_data) with gil: 281 cb = <object>cb_data 282 r = cb.end_func(cb.user_data) 283 return r if r != None else 0 284 285 286cdef void _err_handler(int num, const_char *msg, const_char *where) with gil: 287 # can't raise exception in cdef callback function, so use a global variable 288 # instead 289 global __exception 290 __exception = ServerError(num, <char*>msg, None) 291 if where: __exception.where = <char*>where 292 293 294# decorator to register callbacks 295 296class make_method: 297 """ 298 A decorator that serves as a more convenient alternative to 299 :meth:`Server.add_method()`. 300 """ 301 # counter to keep track of the order in which the callback functions where 302 # defined 303 _counter = 0 304 305 def __init__(self, path, types, user_data=None): 306 """ 307 make_method(path, typespec[, user_data]) 308 309 Set the path and argument types for which the decorated method 310 is to be registered. 311 312 :param path: 313 the message path to be handled by the registered method. 314 ``None`` may be used as a wildcard to match any OSC message. 315 :param typespec: 316 the argument types to be handled by the registered method. 317 ``None`` may be used as a wildcard to match any OSC message. 318 :param user_data: 319 An arbitrary object that will be passed on to the decorated 320 method every time a matching message is received. 321 """ 322 self.spec = struct(counter=make_method._counter, 323 path=path, 324 types=types, 325 user_data=user_data) 326 make_method._counter += 1 327 328 def __call__(self, f): 329 # we can't access the Server object here, because at the time the 330 # decorator is run it doesn't even exist yet, so we store the 331 # path/typespec in the function object instead... 332 if not hasattr(f, '_method_spec'): 333 f._method_spec = [] 334 f._method_spec.append(self.spec) 335 return f 336 337 338# common base class for both Server and ServerThread 339 340cdef class _ServerBase: 341 cdef lo_server _server 342 cdef list _keep_refs 343 344 def __init__(self, **kwargs): 345 self._keep_refs = [] 346 347 if 'reg_methods' not in kwargs or kwargs['reg_methods']: 348 self.register_methods() 349 350 cdef _check(self): 351 if self._server == NULL: 352 raise RuntimeError("Server method called after free()") 353 354 def register_methods(self, obj=None): 355 """ 356 register_methods(obj=None) 357 358 Call :meth:`add_method()` for all methods of an object that are 359 decorated with :func:`make_method`. 360 361 :param obj: 362 The object that implements the OSC callbacks to be registered. 363 By default this is the server object itself. 364 365 This function is usually called automatically by the server's 366 constructor, unless its *reg_methods* parameter was set to ``False``. 367 """ 368 if obj == None: 369 obj = self 370 # find and register methods that were defined using decorators 371 methods = [] 372 for m in _inspect.getmembers(obj): 373 if hasattr(m[1], '_method_spec'): 374 for spec in m[1]._method_spec: 375 methods.append(struct(spec=spec, name=m[1])) 376 # sort by counter 377 methods.sort(key=lambda x: x.spec.counter) 378 for e in methods: 379 self.add_method(e.spec.path, e.spec.types, e.name, e.spec.user_data) 380 381 def get_url(self): 382 self._check() 383 cdef char *tmp = lo_server_get_url(self._server) 384 cdef object r = tmp 385 free(tmp) 386 return _decode(r) 387 388 def get_port(self): 389 self._check() 390 return lo_server_get_port(self._server) 391 392 def get_protocol(self): 393 self._check() 394 return lo_server_get_protocol(self._server) 395 396 def fileno(self): 397 """ 398 Return the file descriptor of the server socket, or -1 if not 399 supported by the underlying server protocol. 400 """ 401 self._check() 402 return lo_server_get_socket_fd(self._server) 403 404 def add_method(self, path, typespec, func, user_data=None): 405 """ 406 add_method(path, typespec, func, user_data=None) 407 408 Register a callback function for OSC messages with matching path and 409 argument types. 410 411 :param path: 412 the message path to be handled by the registered method. 413 ``None`` may be used as a wildcard to match any OSC message. 414 415 :param typespec: 416 the argument types to be handled by the registered method. 417 ``None`` may be used as a wildcard to match any OSC message. 418 419 :param func: 420 the callback function. This may be a global function, a class 421 method, or any other callable object, pyliblo will know what 422 to do either way. 423 424 :param user_data: 425 An arbitrary object that will be passed on to *func* every time 426 a matching message is received. 427 """ 428 cdef char *p 429 cdef char *t 430 431 if isinstance(path, (bytes, unicode)): 432 s = _encode(path) 433 p = s 434 elif path == None: 435 p = NULL 436 else: 437 raise TypeError("path must be a string or None") 438 439 if isinstance(typespec, (bytes, unicode)): 440 s2 = _encode(typespec) 441 t = s2 442 elif typespec == None: 443 t = NULL 444 else: 445 raise TypeError("typespec must be a string or None") 446 447 self._check() 448 449 # use a weak reference if func is a method, to avoid circular 450 # references in cases where func is a method of an object that also 451 # has a reference to the server (e.g. when deriving from the Server 452 # class) 453 cb = struct(func=_weakref_method(func), user_data=user_data) 454 # keep a reference to the callback data around 455 self._keep_refs.append(cb) 456 457 lo_server_add_method(self._server, p, t, _msg_callback, <void*>cb) 458 459 def del_method(self, path, typespec): 460 """ 461 del_method(path, typespec) 462 463 Delete a callback function. For both *path* and *typespec*, ``None`` 464 may be used as a wildcard. 465 466 .. versionadded:: 0.9.2 467 """ 468 cdef char *p 469 cdef char *t 470 471 if isinstance(path, (bytes, unicode)): 472 s = _encode(path) 473 p = s 474 elif path == None: 475 p = NULL 476 else: 477 raise TypeError("path must be a string or None") 478 479 if isinstance(typespec, (bytes, unicode)): 480 s2 = _encode(typespec) 481 t = s2 482 elif typespec == None: 483 t = NULL 484 else: 485 raise TypeError("typespec must be a string or None") 486 487 self._check() 488 lo_server_del_method(self._server, p, t) 489 490 def add_bundle_handlers(self, start_handler, end_handler, user_data=None): 491 """ 492 add_bundle_handlers(start_handler, end_handler, user_data=None) 493 494 Add bundle notification handlers. 495 496 :param start_handler: 497 a callback which fires when at the start of a bundle. This is 498 called with the bundle's timestamp and user_data. 499 :param end_handler: 500 a callback which fires when at the end of a bundle. This is called 501 with user_data. 502 :param user_data: 503 data to pass to the handlers. 504 505 .. versionadded:: 0.10.0 506 """ 507 cb_data = struct(start_func=_weakref_method(start_handler), 508 end_func=_weakref_method(end_handler), 509 user_data=user_data) 510 self._keep_refs.append(cb_data) 511 512 lo_server_add_bundle_handlers(self._server, _bundle_start_callback, 513 _bundle_end_callback, <void*>cb_data) 514 515 def send(self, target, *args): 516 """ 517 send(target, *messages) 518 send(target, path, *args) 519 520 Send a message or bundle from this server to the the given target. 521 Arguments may be one or more :class:`Message` or :class:`Bundle` 522 objects, or a single message given by its path and optional arguments. 523 524 :param target: 525 the address to send the message to; an :class:`Address` object, 526 a port number, a ``(hostname, port)`` tuple, or a URL. 527 :param messages: 528 one or more objects of type :class:`Message` or :class:`Bundle`. 529 :param path: 530 the path of the message to be sent. 531 532 :raises AddressError: 533 if the given target is invalid. 534 :raises IOError: 535 if the message couldn't be sent. 536 """ 537 self._check() 538 _send(target, self, args) 539 540 property url: 541 """ 542 The server's URL. 543 """ 544 def __get__(self): 545 return self.get_url() 546 547 property port: 548 """ 549 The server's port number. 550 """ 551 def __get__(self): 552 return self.get_port() 553 554 property protocol: 555 """ 556 The server's protocol (one of the constants :const:`UDP`, 557 :const:`TCP`, or :const:`UNIX`). 558 """ 559 def __get__(self): 560 return self.get_protocol() 561 562 563cdef class Server(_ServerBase): 564 """ 565 A server that can receive OSC messages using a simple single-threaded 566 polling model. 567 Use :class:`ServerThread` for an OSC server that runs in its own thread 568 and never blocks. 569 """ 570 def __init__(self, port=None, proto=LO_DEFAULT, **kwargs): 571 """ 572 Server(port[, proto]) 573 574 Create a new :class:`!Server` object. 575 576 :param port: 577 a decimal port number or a UNIX socket path. If omitted, an 578 arbitrary free UDP port will be used. 579 :param proto: 580 one of the constants :const:`UDP`, :const:`TCP`, or :const:`UNIX`; 581 default is :const:`UDP`. 582 583 :keyword reg_methods: 584 ``False`` if you don't want the init function to automatically 585 register callbacks defined with the :func:`make_method` decorator 586 (keyword argument only). 587 588 Exceptions: ServerError 589 """ 590 cdef char *cs 591 592 if port != None: 593 p = _encode(str(port)); 594 cs = p 595 else: 596 cs = NULL 597 598 global __exception 599 __exception = None 600 self._server = lo_server_new_with_proto(cs, proto, _err_handler) 601 if __exception: 602 raise __exception 603 604 _ServerBase.__init__(self, **kwargs) 605 606 def __dealloc__(self): 607 self.free() 608 609 def free(self): 610 """ 611 Free the underlying server object and close its port. Note that this 612 will also happen automatically when the server is deallocated. 613 """ 614 if self._server: 615 lo_server_free(self._server) 616 self._server = NULL 617 618 def recv(self, timeout=None): 619 """ 620 recv(timeout=None) 621 622 Receive and dispatch one OSC message. Blocking by default, unless 623 *timeout* is specified. 624 625 :param timeout: 626 Time in milliseconds after which the function returns if no 627 messages have been received. 628 *timeout* may be 0, in which case the function always returns 629 immediately, whether messages have been received or not. 630 631 :return: 632 ``True`` if a message was received, otherwise ``False``. 633 """ 634 cdef int t, r 635 self._check() 636 if timeout != None: 637 t = timeout 638 with nogil: 639 r = lo_server_recv_noblock(self._server, t) 640 return r and True or False 641 else: 642 with nogil: 643 lo_server_recv(self._server) 644 return True 645 646 647cdef class ServerThread(_ServerBase): 648 """ 649 Unlike :class:`Server`, :class:`!ServerThread` uses its own thread which 650 runs in the background to dispatch messages. 651 :class:`!ServerThread` has the same methods as :class:`!Server`, with the 652 exception of :meth:`Server.recv`. Instead, it defines two additional 653 methods :meth:`start` and :meth:`stop`. 654 655 .. note:: Because liblo creates its own thread to receive and dispatch 656 messages, callback functions will not be run in the main Python 657 thread! 658 """ 659 cdef lo_server_thread _server_thread 660 661 def __init__(self, port=None, proto=LO_DEFAULT, **kwargs): 662 """ 663 ServerThread(port[, proto]) 664 665 Create a new :class:`!ServerThread` object, which can receive OSC messages. 666 Unlike :class:`Server`, :class:`ServerThread` uses its own thread which 667 runs in the background to dispatch messages. Note that callback methods 668 will not be run in the main Python thread! 669 670 :param port: 671 a decimal port number or a UNIX socket path. If omitted, an 672 arbitrary free UDP port will be used. 673 :param proto: 674 one of the constants :const:`UDP`, :const:`TCP`, or :const:`UNIX`; 675 default is :const:`UDP`. 676 677 :keyword reg_methods: 678 ``False`` if you don't want the init function to automatically 679 register callbacks defined with the make_method decorator 680 (keyword argument only). 681 682 :raises ServerError: 683 if creating the server fails, e.g. because the given port could not 684 be opened. 685 """ 686 cdef char *cs 687 688 if port != None: 689 p = _encode(str(port)); 690 cs = p 691 else: 692 cs = NULL 693 694 # make sure python can handle threading 695 PyEval_InitThreads() 696 697 global __exception 698 __exception = None 699 self._server_thread = lo_server_thread_new_with_proto(cs, proto, _err_handler) 700 if __exception: 701 raise __exception 702 self._server = lo_server_thread_get_server(self._server_thread) 703 704 _ServerBase.__init__(self, **kwargs) 705 706 def __dealloc__(self): 707 self.free() 708 709 def free(self): 710 """ 711 Free the underlying server object and close its port. Note that this 712 will also happen automatically when the server is deallocated. 713 """ 714 if self._server_thread: 715 lo_server_thread_free(self._server_thread) 716 self._server_thread = NULL 717 self._server = NULL 718 719 def start(self): 720 """ 721 Start the server thread. liblo will now start to dispatch any messages 722 it receives. 723 """ 724 self._check() 725 lo_server_thread_start(self._server_thread) 726 727 def stop(self): 728 """ 729 Stop the server thread. 730 """ 731 self._check() 732 lo_server_thread_stop(self._server_thread) 733 734 735################################################################################ 736# Address 737################################################################################ 738 739class AddressError(Exception): 740 """ 741 Raised when trying to create an invalid :class:`Address` object. 742 """ 743 def __init__(self, msg): 744 self.msg = msg 745 def __str__(self): 746 return "address error: %s" % self.msg 747 748 749cdef class Address: 750 cdef lo_address _address 751 752 def __init__(self, addr, addr2=None, proto=LO_UDP): 753 """ 754 Address(hostname, port[, proto]) 755 Address(port) 756 Address(url) 757 758 Create a new :class:`!Address` object from the given hostname/port 759 or URL. 760 761 :param hostname: 762 the target's hostname. 763 764 :param port: 765 the port number on the target. 766 767 :param proto: 768 one of the constants :const:`UDP`, :const:`TCP`, or :const:`UNIX`. 769 770 :param url: 771 a URL in liblo notation, e.g. ``'osc.udp://hostname:1234/'``. 772 773 :raises AddressError: 774 if the given parameters do not represent a valid address. 775 776 """ 777 if addr2: 778 # Address(host, port[, proto]) 779 s = _encode(addr) 780 s2 = _encode(str(addr2)) 781 self._address = lo_address_new_with_proto(proto, s, s2) 782 if not self._address: 783 raise AddressError("invalid protocol") 784 elif isinstance(addr, int) or (isinstance(addr, str) and addr.isdigit()): 785 # Address(port) 786 s = str(addr).encode() 787 self._address = lo_address_new(NULL, s) 788 else: 789 # Address(url) 790 s = _encode(addr) 791 self._address = lo_address_new_from_url(s) 792 # lo_address_errno() is of no use if self._addr == NULL 793 if not self._address: 794 raise AddressError("invalid URL '%s'" % str(addr)) 795 796 def __dealloc__(self): 797 lo_address_free(self._address) 798 799 def get_url(self): 800 cdef char *tmp = lo_address_get_url(self._address) 801 cdef object r = tmp 802 free(tmp) 803 return _decode(r) 804 805 def get_hostname(self): 806 return _decode(lo_address_get_hostname(self._address)) 807 808 def get_port(self): 809 cdef bytes s = lo_address_get_port(self._address) 810 if s.isdigit(): 811 return int(s) 812 else: 813 return _decode(s) 814 815 def get_protocol(self): 816 return lo_address_get_protocol(self._address) 817 818 property url: 819 """ 820 The address's URL. 821 """ 822 def __get__(self): 823 return self.get_url() 824 825 property hostname: 826 """ 827 The address's hostname. 828 """ 829 def __get__(self): 830 return self.get_hostname() 831 832 property port: 833 """ 834 The address's port number. 835 """ 836 def __get__(self): 837 return self.get_port() 838 839 property protocol: 840 """ 841 The address's protocol (one of the constants :const:`UDP`, 842 :const:`TCP`, or :const:`UNIX`). 843 """ 844 def __get__(self): 845 return self.get_protocol() 846 847 848################################################################################ 849# Message 850################################################################################ 851 852cdef class _Blob: 853 cdef lo_blob _blob 854 855 def __init__(self, arr): 856 # arr can by any sequence type 857 cdef unsigned char *p 858 cdef uint32_t size, i 859 size = len(arr) 860 if size < 1: 861 raise ValueError("blob is empty") 862 # copy each element of arr to a C array 863 p = <unsigned char*>malloc(size) 864 try: 865 if isinstance(arr[0], (str, unicode)): 866 # use ord() if arr is a string (but not bytes) 867 for i from 0 <= i < size: 868 p[i] = ord(arr[i]) 869 else: 870 for i from 0 <= i < size: 871 p[i] = arr[i] 872 # build blob 873 self._blob = lo_blob_new(size, p) 874 finally: 875 free(p) 876 877 def __dealloc__(self): 878 lo_blob_free(self._blob) 879 880 881cdef class Message: 882 """ 883 An OSC message, consisting of a path and arbitrary arguments. 884 """ 885 cdef bytes _path 886 cdef lo_message _message 887 cdef list _keep_refs 888 889 def __init__(self, path, *args): 890 """ 891 Message(path, *args) 892 893 Create a new :class:`!Message` object. 894 """ 895 self._keep_refs = [] 896 # encode path to bytestring if necessary 897 self._path = _encode(path) 898 self._message = lo_message_new() 899 900 self.add(*args) 901 902 def __dealloc__(self): 903 lo_message_free(self._message) 904 905 def add(self, *args): 906 """ 907 add(*args) 908 909 Append the given arguments to the message. 910 Arguments can be single values or ``(typetag, data)`` tuples. 911 """ 912 for arg in args: 913 if (isinstance(arg, tuple) and len(arg) <= 2 and 914 isinstance(arg[0], (bytes, unicode)) and len(arg[0]) == 1): 915 # type explicitly specified 916 if len(arg) == 2: 917 self._add(arg[0], arg[1]) 918 else: 919 self._add(arg[0], None) 920 else: 921 # detect type automatically 922 self._add_auto(arg) 923 924 cdef _add(self, type, value): 925 cdef uint8_t midi[4] 926 927 # accept both bytes and unicode as type specifier 928 cdef char t = ord(_decode(type)[0]) 929 930 if t == 'i': 931 lo_message_add_int32(self._message, int(value)) 932 elif t == 'h': 933 lo_message_add_int64(self._message, long(value)) 934 elif t == 'f': 935 lo_message_add_float(self._message, float(value)) 936 elif t == 'd': 937 lo_message_add_double(self._message, float(value)) 938 elif t == 'c': 939 lo_message_add_char(self._message, ord(value)) 940 elif t == 's': 941 s = _encode(value) 942 lo_message_add_string(self._message, s) 943 elif t == 'S': 944 s = _encode(value) 945 lo_message_add_symbol(self._message, s) 946 elif t == 'T': 947 lo_message_add_true(self._message) 948 elif t == 'F': 949 lo_message_add_false(self._message) 950 elif t == 'N': 951 lo_message_add_nil(self._message) 952 elif t == 'I': 953 lo_message_add_infinitum(self._message) 954 elif t == 'm': 955 for n from 0 <= n < 4: 956 midi[n] = value[n] 957 lo_message_add_midi(self._message, midi) 958 elif t == 't': 959 lo_message_add_timetag(self._message, _double_to_timetag(value)) 960 elif t == 'b': 961 b = _Blob(value) 962 # make sure the blob is not deleted as long as this message exists 963 self._keep_refs.append(b) 964 lo_message_add_blob(self._message, (<_Blob>b)._blob) 965 else: 966 raise TypeError("unknown OSC data type '%c'" % t) 967 968 cdef _add_auto(self, value): 969 # bool is a subclass of int, so check those first 970 if value is True: 971 lo_message_add_true(self._message) 972 elif value is False: 973 lo_message_add_false(self._message) 974 elif isinstance(value, (int, long)): 975 try: 976 lo_message_add_int32(self._message, <int32_t>value) 977 except OverflowError: 978 lo_message_add_int64(self._message, <int64_t>value) 979 elif isinstance(value, float): 980 lo_message_add_float(self._message, float(value)) 981 elif isinstance(value, (bytes, unicode)): 982 s = _encode(value) 983 lo_message_add_string(self._message, s) 984 elif value == None: 985 lo_message_add_nil(self._message) 986 elif value == float('inf'): 987 lo_message_add_infinitum(self._message) 988 else: 989 # last chance: could be a blob 990 try: 991 iter(value) 992 except TypeError: 993 raise TypeError("unsupported message argument type") 994 self._add('b', value) 995 996 997################################################################################ 998# Bundle 999################################################################################ 1000 1001cdef class Bundle: 1002 """ 1003 A bundle of one or more messages to be sent and dispatched together. 1004 """ 1005 cdef lo_bundle _bundle 1006 cdef list _keep_refs 1007 1008 def __init__(self, *messages): 1009 """ 1010 Bundle([timetag, ]*messages) 1011 1012 Create a new :class:`Bundle` object. You can optionally specify a 1013 time at which the messages should be dispatched (as an OSC timetag 1014 float), and any number of messages to be included in the bundle. 1015 """ 1016 cdef lo_timetag tt 1017 tt.sec, tt.frac = 0, 0 1018 self._keep_refs = [] 1019 1020 if len(messages) and not isinstance(messages[0], Message): 1021 t = messages[0] 1022 if isinstance(t, (float, int, long)): 1023 tt = _double_to_timetag(t) 1024 elif isinstance(t, tuple) and len(t) == 2: 1025 tt.sec, tt.frac = t 1026 else: 1027 raise TypeError("invalid timetag") 1028 # first argument was timetag, so continue with second 1029 messages = messages[1:] 1030 1031 self._bundle = lo_bundle_new(tt) 1032 if len(messages): 1033 self.add(*messages) 1034 1035 def __dealloc__(self): 1036 lo_bundle_free(self._bundle) 1037 1038 def add(self, *args): 1039 """ 1040 add(*messages) 1041 add(path, *args) 1042 1043 Add one or more messages to the bundle. 1044 """ 1045 if isinstance(args[0], Message): 1046 # args is already a list of Messages 1047 messages = args 1048 else: 1049 # make a single Message from all arguments 1050 messages = [Message(*args)] 1051 1052 # add all messages 1053 for m in messages: 1054 self._keep_refs.append(m) 1055 message = <Message> m 1056 lo_bundle_add_message(self._bundle, message._path, message._message) 1057