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