******************** Messages and Bundles ******************** .. toctree:: :maxdepth: 2 .. py:module:: osc4py3.oscbuildparse .. note:: OSC structures are defined in module :mod:`oscbuildparse`. This module provides all low level tools to manipulate these structures, build them, encode them to raw OSC packets, and decode them back. You may specially be interested by message construction in the examples (encoding and decoding is normally done automatically for you by :mod:`osc4py3`). This module is only here to translate OSC packets from/to Python values. It can be reused anywhere as is (only depend on Python3 standard modules). Examples:: >>> from osc4py3.oscbuildparse import * >>> dir() ['OSCBundle', 'OSCCorruptedRawError', 'OSCError', 'OSCInternalBugError', 'OSCInvalidDataError', 'OSCInvalidRawError', 'OSCInvalidSignatureError', 'OSCMessage', 'OSCUnknownTypetagError', 'OSC_BANG', 'OSC_IMMEDIATELY', 'OSC_IMPULSE', 'OSC_INFINITUM', 'OSCbang', 'OSCmidi', 'OSCrgba', 'OSCtimetag', '__builtins__', '__doc__', '__name__', '__package__', 'decode_packet', 'dumphex_buffer', 'encode_packet', 'float2timetag', 'timetag2float', 'timetag2unixtime', 'unixtime2timetag'] >>> msg = OSCMessage('/my/pattern',',iisf',[1,3,"a string",11.3]) >>> raw = encode_packet(msg) >>> dumphex_buffer(raw) 000:2f6d792f 70617474 65726e00 2c696973 /my/ patt ern. ,iis 016:66000000 00000001 00000003 61207374 f... .... .... a st 032:72696e67 00000000 4134cccd ring .... A4.. >>> decode_packet(raw) OSCMessage(addrpattern='/my/pattern', typetags=',iisf', arguments=(1, 3, 'a string', 11.300000190734863)) >>> import time >>> bun = OSCBundle(unixtime2timetag(time.time()+1), [OSCMessage("/first/message",",ii",[1,2]), OSCMessage("/second/message",",fT",[4.5,True])]) >>> raw = encode_packet(bun) >>> dumphex_buffer(raw) 000:2362756e 646c6500 d2c3e04f 455a9000 #bun dle. ...O EZ.. 016:0000001c 2f666972 73742f6d 65737361 .... /fir st/m essa 032:67650000 2c696900 00000001 00000002 ge.. ,ii. .... .... 048:00000018 2f736563 6f6e642f 6d657373 .... /sec ond/ mess 064:61676500 2c665400 40900000 age. ,fT. @... >>> decode_packet(raw) OSCBundle(timetag=OSCtimetag(sec=3536052435, frac=3018637312), elements=(OSCMessage(addrpattern='/first/message', typetags=',ii', arguments=(1, 2)), OSCMessage(addrpattern='/second/message', typetags=',fT', arguments=(4.5, True)))) >>> msg = OSCMessage("/shortcut/with/typedetection", None, [True, OSC_BANG, 12, 11.3]) >>> raw = encode_packet(msg) >>> dumphex_buffer(raw) 000:2f73686f 72746375 742f7769 74682f74 /sho rtcu t/wi th/t 016:79706564 65746563 74696f6e 00000000 yped etec tion .... 032:2c544969 66000000 0000000c 4134cccd ,TIi f... .... A4.. >>> decode_packet(raw) OSCMessage(addrpattern='/shortcut/with/typedetection', typetags=',TIif', arguments=(True, OSCbang(), 12, 11.300000190734863)) Note : you can find other examples in :file:`osc4py3/tests/buildparse.py` module. Programming interface ===================== There are two main functions for advanced users: - :func:`encode_packet` build the binary representation for OSC data - :func:`decode_packet` retrieve OSC data from binary representation An OSC packet can either be an OSC message, or an OSC bundle (which contains a collection of messages and bundles - recursively if needed). .. _message: OSC Messages ------------ For developer point of view, there is an :class:`OSCMessage` named tuple class which is used as container to encode and decode messages. It contains fields accordingly to OSC1.1 protocol: .. autoclass:: osc4py3.oscbuildparse.OSCMessage The ``typetag`` must start by a comma (``','``) and use a set of chars to describe OSC defined data types, as listed in the `Supported atomic data types`_ table. It may optionally be set to :const:`None` for an automatic detection of type tags from values (see `Automatic type tagging`_ for detection rules). .. _bundle: OSC Bundles ----------- And to add a time tag or group several messages in a packet, there is an :class:`OSCBundle` named tuple which is used to encode and decode bundles. It contains fields accordingly to OSC1.1 protocol: .. autoclass:: osc4py3.oscbuildparse.OSCBundle Its first ``timetag`` field must be set to :const:`osc4py3.oscbuildparse.OSC_IMMEDIATELY` to request an immediate processing of the bundle messages by the server's matching handlers. Else, it is considered as an OSC time (see :ref:`timetag`) and must be computed for planned processing time. .. _typecodes: Supported atomic data types --------------------------- In addition to the required OSC1.1 ``ifsbtTFNI`` type tag chars, we support optional types of OSC1.0 protocol ``hdScrm[]`` (support for new types is easy to add if necessary). .. attention:: Check that programs receiving your data also support optional data types. You may use the :ref:`oob options` :ref:`restrict_typetags` to limit the data types manipulated by :mod:`osc4py3`. .. list-table:: Type codes :header-rows: 1 :widths: 10,20,20,50 * - Tag - Data - Python - Notes * - i - int32 - ``int`` - signed `integer`_ * - f - float32 - ``float`` - a C float (4 bytes) * - s - string - ``str`` - ASCII `string `_ * - b - blob - ``memoryview`` - mapping to part in received `blob`_ data * - h - int64 - ``int`` - to transmit larger integers * - t - timetag - :class:`OSCtimetag` - two bytes named tuple `time `_ * - d - float64 - ``float`` - a C double (8 bytes) * - S - alt-string - ``str`` - ASCII strings to distinguish with 's' `strings `_ * - c - ascii-char - ``str`` - one ASCII `char `_ * - r - rgba-color - :class:`OSCrgba` - four bytes fields named tuple `RGBA data`_ * - m - midi-msg - :class:`OSCmidi` - four bytes fields named tuple `MIDI data`_ * - T - (none) - ``True`` - direct True value only with type tag * - F - (none) - ``False`` - direct False value only with type tag * - N - (none) - :const:`None` - 'nil' in OSC only with type tag * - I - (none) - :class:`OSCbang` - named tuple with no field (see `bang`_) * - [ - (none) - ``tuple`` - beginning of an `array`_ * - ] - (none) - - end of the `array`_ (to terminate tuple) Integer ^^^^^^^ Care that Python int has a range with "no limit". Overflows will only be detected when trying to pack such values into the 32 bits representation of an OSC packet integer. .. _text: String and char ^^^^^^^^^^^^^^^ They are normally transmitted as ASCII, default processing ensure this encoding (with ``strict`` error handling, raising an exception if a non-ASCII char is in a string). An ``oob`` option (see `oob options`_ below) allows to specify an encoding. The value for a string/char is normally a Python ``str`` ; you can give a ``bytes`` or ``bytearray`` or ``memoryview``, but they must not contain a zero byte (except at the end - and it will be used a string termination when decoding). For a char, the ``string`` / ``bytes`` / … must contain only one element. In OSC, distinction between ``s`` strings and ``S`` strings are application meaning defined. And in :mod:`osc4py3` you don't have direct access to data type codes (you may request to get the . Blob ^^^^ They allow transmission of any binary data of your own. The value for a blob can be ``bytes`` or ``bytearray`` or ``memoryview``. When getting blob values on packet reception, they are returned as ``memoryview`` objects to avoid extra data copying. You may cast them to ``bytes`` if you want to copy/extract the value from the OSC packet. .. _bang: Infinitum / Impulse / Bang ^^^^^^^^^^^^^^^^^^^^^^^^^^ The Infinitum data (``'I'``), renamed as Impulse 'bang' in OSC 1.1, is returned in Python as a :class:`OSCbang` named tuple (with no value in the tuple). Constant :const:`OSC_INFINITUM` is defined as an :class:`OSCbang` value, and aliases constants :class:`OSC_IMPULSE` and :class:`OSC_BANG` are also defined. You should test on object class with :code:`isinstance(x,OSCbang)`. .. _timetag: Time Tag ^^^^^^^^ As time tag is stored into an :class:`OSCtimetag` named tuple with two items. .. autoclass:: osc4py3.oscbuildparse.OSCtimetag As it is not really usable with usual Python time, four conversion functions have been defined: - :func:`timetag2float` convert an :class:`OSCtimetag` tuple into a float value in seconds from 1/1/1900, - :func:`timetag2unixtime` convert an :class:`OSCtimetag` tuple into a Unix float time in seconds from 1/1/1970 (Python time), - :func:`float2timetag` convert a float value of seconds from 1/1/1900 into an :class:`OSCtimetag` tuple, - :func:`unixtime2timetag` convert a Unix float value of seconds from 1/1/1970 (Python time) into an :class:`OSCtimetag` tuple - can be used The special value used in OSC to indicate an "immediate" time, with a time tag having 0 in seconds field and 1 in factional part field (represented as 0x00000001 value), is available for comparison and usage in constant :const:`osc4py3.oscbuildparse.OSC_IMMEDIATELY`. Array ^^^^^ An array is a way to group some data in the OSC message arguments. On the Python side an array is simply a list or a tuple of values. By example, to create a message with two int followed by four grouped int, you will have: - Type tags string: :code:`',ii[iiii]'` - Arguments list: :code:`[3, 1, [4, 2, 8, 9]]` Note : When decoding a message, array arguments are returned as tuple, not list. In this example: :code:`(3, 1, (4, 2, 8, 9))`. RGBA data ^^^^^^^^^ RGBA values are stored into an :class:`OSCrgba` named tuple containing four single byte values (int in 0..255 range): 0. ``red`` 1. ``green`` 2. ``blue`` 3. ``alpha`` MIDI data ^^^^^^^^^ MIDI values are stored into an :class:`OSCmidi` named tuple containing four single byte values (int in 0..255 range): 0. ``portid`` 1. ``status`` 2. ``data1`` 3. ``data2`` OSCbang = namedtuple('OSCbang', '') .. _automatic type: Automatic type tagging ---------------------- When creating an :class:`OSCMessage`, you can give a ``None`` value as typetags. Then, message arguments are automatically parsed to identify their types and build the type tags string for you. The following mapping is used: .. list-table:: Automatic typing :header-rows: 1 * - What - Type tag and corresponding data * - value :const:`None` - ``N`` without data * - value :const:`True` - ``T`` without data * - value :const:`False` - ``F`` without data * - type ``int`` - ``i`` with int32 * - type ``float`` - ``f`` with float32 * - type ``str`` - ``s`` with string * - type ``bytes`` - ``b`` with raw binary * - type ``bytearray`` - ``b`` with raw binary * - type ``memoryview`` - ``b`` with raw binary * - type :class:`OSCrgba` - ``r`` with four byte values * - type :class:`OSCmidi` - ``m`` with four byte values * - type :class:`OSCbang` - ``I`` without data * - type :class:`OSCtimetag` - ``t`` with two int32 .. _errors: Errors ====== All errors explicitly raised by the module use specify hierarchy of exceptions:: Exception OSCError OSCCorruptedRawError OSCInternalBugError OSCInvalidDataError OSCInvalidRawError OSCInvalidSignatureError OSCUnknownTypetagError OSCError -------- This is the parent class for OSC errors, usable as a catchall for all errors related to this module. OSCInvalidDataError ------------------- There is a problem in some OSC data provided for encoding to raw OSC representation. OSCInvalidRawError ------------------ There is a problem in a raw OSC buffer when decoding it. OSCInternalBugError ------------------- Hey, we detected a bug in OSC module. Please, signal it with description of the context, data processed, options used. OSCUnknownTypetagError ---------------------- Found an invalid (unknown) type tag when encoding or decoding. This include type tags not in a subset with :ref:`restrict_typetags` option. OSCInvalidSignatureError ------------------------ Check of raw data with signature failed due bad source or modified data. This can only occur with advanced packet control enabled and signature functions installed in out-of-band. OSCCorruptedRawError -------------------- Check of raw data with checksum failed. This can only occur with advanced packet control enabled and checksum functions installed in out-of-band. .. _oob options: Out of band options =================== .. warning:: Out of band options may need further debugging. These OOB options are transmitted as a simple Python dict among internal :mod:`oscbuildparse` functions to enable and define parameters of extra processing. You may add your own keys in this dict to transmit data to your extra processing functions. Supported data types -------------------- .. _restrict_typetags: restrict_typetags ^^^^^^^^^^^^^^^^^ :code:`oob['restrict_typetags']` must contain a string with the subset typecode chars you want to allow (in ``ifsbtTFNIhdScrm[]``) — see :ref:`typecodes`. This make your program ensure that it don't use data types unsupported by other OSC implementations. Strings encoding ---------------- The OSC standard encode strings as :abbr:`ASCII (American Standard Code for Information Interchange)` only chars, which include control chars (codes 1 to 31 and 127; code 0 is used as end of string marker), whitespace and following printable chars: !"#$%&'()*+,-./ 0123456789 :;<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ [\]^_` abcdefghijklmnopqrstuvwxyz {|}~ This is how :mod:`osc4py3` works, converting all Unicode Python strings into an ASCII encoding. By default the encoding and decoding use ``strict`` error handling scheme: any out of ASCII char in a Python string raise an :class:`UnicodeEncodeError` when encoding to OSC sctring, and any non-ASCII char in an OSC string coming from somewhere raise an :class:`UnicodeDecodeError` when decoding to Python string. You can modify the encoding to use and the error processing scheme (``strict``, ``replace``, ``ignore``…) with following OOB options. .. caution:: Changing strings encoding to non-ASCII goes out of OSC standards, you may brake communications with other OSC implementations. It may be better to transmit encoded text in blobs and to agree on encoding on both sides (ex. transmit encoding in a separate OSC string). str_decode ^^^^^^^^^^ :code:`oob['str_decode']` must contain a tuple of two values to specify how to decode OSC strings. First item is the encoding to use, second item the error handling scheme. Default to :code:`('ascii', 'strict')`. str_encode ^^^^^^^^^^ :code:`oob['str_encode']` must contain a tuple of two values to specify how to encode OSC strings. First item is the encoding to use, second item the error handling scheme. Default to :code:`('ascii', 'strict')`. char_decode ^^^^^^^^^^^ :code:`oob['char_decode']` must contain a tuple of two values to specify how to decode OSC char. First item is the encoding to use, second item the error handling scheme. Default to :code:`('ascii', 'strict')`. char_encode ^^^^^^^^^^^ :code:`oob['char_encode']` must contain a tuple of two values to specify how to encode OSC char. First item is the encoding to use, second item the error handling scheme. Default to :code:`('ascii', 'strict')`. Compression of addresses ------------------------ .. note:: The :mod:`osc4py3` package implement support for OSC address compression as presented in `Improving the Efficiency of Open Sound Control (OSC) with Compressed Address Strings`_ (SMC 2011, by Jari Kleimola and Patrick J. McGlynn). .. _Improving the Efficiency of Open Sound Control (OSC) with Compressed Address Strings: http://research.spa.aalto.fi/publications/papers/smc2011-osc/ Two sides of an OSC communication agree on some int to string mapping. The address is then sent as a single ``"/"`` OSC string followed by a 32 bits int code. This implementation only do compression / decompression of addresses, it's up to the user to exchange int / address mapping — by example via an initial exchange of OSC messages (eventually grouped in a bundle). addrpattern_decompression ^^^^^^^^^^^^^^^^^^^^^^^^^ :code:`oob['addrpattern_decompression']` must contain the int to string mapping to retrieve message address from int code for incoming packets. addrpattern_compression ^^^^^^^^^^^^^^^^^^^^^^^ :code:`oob['addrpattern_decompression']` must contain the string to int mapping to get int code from message address for outgoing packets. Basic messages controls ----------------------- check_addrpattern ^^^^^^^^^^^^^^^^^ This option allows to check that address string correctly follow OSC pattern. :code:`oob['check_addrpattern']` must be a boolean set to :const:`True` to check that address string correctly follow OSC pattern. force_typetags ^^^^^^^^^^^^^^ This option force presence of type tags in received OSC packets, you can't send an only address message with this option enabled (it must contain at least a ``","`` string indicating no data). :code:`oob['force_typetags']` must be a boolean set to :const:`True` to force presence of a type tags specification, even with no data (in such case type tags simply contains ``","`` string). Dump of packets --------------- decode_packet_dumpraw ^^^^^^^^^^^^^^^^^^^^^ :code:`oob['decode_packet_dumpraw']` must be a boolean set to :const:`True` to enable file writing of raw OSC packets in hexadecimal representation. encode_packet_dumpraw ^^^^^^^^^^^^^^^^^^^^^ :code:`oob['encode_packet_dumpraw']` must be a boolean set to :const:`True` to enable file writing of raw OSC packets in hexadecimal representation. decode_packet_dumpacket ^^^^^^^^^^^^^^^^^^^^^^^ :code:`oob['decode_packet_dumpacket']` must be a boolean set to :const:`True` to enable file writing of decoded OSC packets representation (OSCMessage or OSCBundle). encode_packet_dumpacket ^^^^^^^^^^^^^^^^^^^^^^^ :code:`oob['encode_packet_dumpacket']` must be a boolean set to :const:`True` to enable file writing of encoded OSC packets representation (OSCMessage or OSCBundle). dump_decoded_values ^^^^^^^^^^^^^^^^^^^ :code:`oob['dump_decoded_values']` must be a boolean set to :const:`True` to enable file writing of individual fields of decoded message data . dumpfile ^^^^^^^^ :code:`oob['dumpfile']` must be a writable stream (file…), used to dump OSC packets (raw or decoded) when `decode_packet_dumpraw`_ or `decode_packet_dumpacket`_ `encode_packet_dumpraw`_ or `encode_packet_dumpacket`_ is enabled. If it is not defined, packets are dump to :file:`sys.stdout` stream. Advanced control ---------------- .. note:: Following OOB options have been installed to setup controlled OSC communications, with possible encryption of data, authentication, checksum… In such situation OSC messages have an address string set to ``"/packet"``, and data is a set of 6 data corresponding to: 0. ``cheksumprot`` a string indicating checksum to use 1. ``rawcksum`` a blob with checksum 2. ``authprot`` a string indicating authentication to use 3. ``rawckauth`` a blob with authentication token 4. ``cryptprot`` a string indicating encryption to use 5. ``rawoscdata`` a blob containing (encrypted) data Once the message have passed all steps, a normal OSC message is retrieved (which can go normally in :mod:`osc4py3` pipeline). When receiving a packet, data is decoded then authentified then sumchecked. When sending a packet, data is sumchecked then authentified then encoded. If a support function is not present in the oob, its feature is simply ignored. When advanced control functions receive raw data, it's a memoryview (on the rawoscdata blob). advanced_packet_control ^^^^^^^^^^^^^^^^^^^^^^^ :code:`oob['advanced_packet_control']` must be a boolean set to :const:`True` to enable packets control. packet_crypt_prot ^^^^^^^^^^^^^^^^^^ :code:`oob['packet_crypt_prot']` may contain string indicating encryption protocol to use. It default to empty string. packet_encrypt_fct ^^^^^^^^^^^^^^^^^^ :code:`oob['packet_encrypt_fct']` is a function to encode raw osc data. It is called with the data to encode, indication of the encryption protocol, and the oob. It must return binary representation of encoded data. Call example:: tobuffer = fencrypt(tobuffer, cryptprot, oob) packet_decrypt_fct ^^^^^^^^^^^^^^^^^^ :code:`oob['packet_decrypt_fct']` is a function to decode raw osc data. It is called with the data to decode, indication of the encryption protocol, and the oob. It must return binary representation of decoded data. Call example:: rawoscdata = fdecrypt(rawoscdata, cryptprot, oob) packet_authsign_prot ^^^^^^^^^^^^^^^^^^^^ :code:`oob['packet_authsign_prot']` may contain string indicating authentication protocol to use. It default to empty string. packet_mkauthsign_fct ^^^^^^^^^^^^^^^^^^^^^ :code:`oob['packet_mkauthsign_fct']` is a function to build authentication data. It is called with osc data buffer and the authentication protocol. It must return the authentication value to transmit as a blob compatible data. Call example:: authsign = fauthsign(tobuffer, authprot, oob) packet_ckauthsign_fct ^^^^^^^^^^^^^^^^^^^^^ :code:`oob['packet_ckauthsign_fct']` is a function to check authentication. It is called with (decoded) osc data, the two authentication fields and the oob. It must simply raise an exception if authentication is not proven. Call example:: fckauthsign(rawoscdata, rawckauth, authprot, oob) packet_checksum_prot ^^^^^^^^^^^^^^^^^^^^^ :code:`oob['packet_checksum_prot']` may contain string indicating checksum protocol to use. It default to empty string. packet_mkchecksum_fct ^^^^^^^^^^^^^^^^^^^^^ :code:`oob['packet_mkchecksum_fct']` is a function to build data integrity checksum value. It is called with osc data buffer, the checksum protocol and the oob. It must return the checksum value to transmit as a blob compatible data. Call example:: cksum = fchecksum(tobuffer, cksumprot, oob) packet_ckcheksum_fct ^^^^^^^^^^^^^^^^^^^^ :code:`oob['packet_ckauthsign_fct']` is a function to check data integrity. It is called with (decoded) osc data, the two checksum fields and the oob. It must simply raise an exception if checksum is not verified. Call example:: fchecksumcheck(rawoscdata, rawcksum, cheksumprot, oob) Code documentation ================== .. autofunction:: osc4py3.oscbuildparse.decode_packet .. autofunction:: osc4py3.oscbuildparse.encode_packet .. autofunction:: osc4py3.oscbuildparse.dumphex_buffer .. autofunction:: osc4py3.oscbuildparse.timetag2float .. autofunction:: osc4py3.oscbuildparse.timetag2unixtime .. autofunction:: osc4py3.oscbuildparse.float2timetag .. autofunction:: osc4py3.oscbuildparse.unixtime2timetag