def _parse_datagram(self): try: self._address_regexp, index = osc_types.get_string(self._dgram, 0) if not self._dgram[index:]: # No params is legit, just return now. return # Get the parameters types. type_tag, index = osc_types.get_string(self._dgram, index) if type_tag.startswith(','): type_tag = type_tag[1:] params = [] param_stack = [params] # Parse each parameter given its type. for param in type_tag: if param == "i": # Integer. val, index = osc_types.get_int(self._dgram, index) elif param == "f": # Float. val, index = osc_types.get_float(self._dgram, index) elif param == "s": # String. val, index = osc_types.get_string(self._dgram, index) elif param == "b": # Blob. val, index = osc_types.get_blob(self._dgram, index) elif param == "r": # RGBA. val, index = osc_types.get_rgba(self._dgram, index) elif param == "d": # Timestamp. val, index = osc_types.get_int(self._dgram, index) elif param == "T": # True. val = True elif param == "F": # False. val = False elif param == "[": # Array start. array = [] param_stack[-1].append(array) param_stack.append(array) elif param == "]": # Array stop. if len(param_stack) < 2: raise ParseError( 'Unexpected closing bracket in type tag: {0}'. format(type_tag)) param_stack.pop() # TODO: Support more exotic types as described in the specification. else: logging.warning( 'Unhandled parameter type: {0}'.format(param)) continue if param not in "[]": param_stack[-1].append(val) if len(param_stack) != 1: raise ParseError( 'Missing closing bracket in type tag: {0}'.format( type_tag)) self._parameters = params except osc_types.ParseError as pe: raise ParseError('Found incorrect datagram, ignoring it', pe)
def _parse_contents(self, index: int) -> Any: contents = [] try: # An OSC Bundle Element consists of its size and its contents. # The size is an int32 representing the number of 8-bit bytes in the # contents, and will always be a multiple of 4. The contents are either # an OSC Message or an OSC Bundle. while self._dgram[index:]: # Get the sub content size. content_size, index = osc_types.get_int(self._dgram, index) # Get the datagram for the sub content. content_dgram = self._dgram[index:index + content_size] # Increment our position index up to the next possible content. index += content_size # Parse the content into an OSC message or bundle. if OscBundle.dgram_is_bundle(content_dgram): contents.append(OscBundle(content_dgram)) elif osc_message.OscMessage.dgram_is_message(content_dgram): contents.append(osc_message.OscMessage(content_dgram)) else: logging.warning( "Could not identify content type of dgram %s" % content_dgram) except (osc_types.ParseError, osc_message.ParseError, IndexError) as e: raise ParseError("Could not parse a content datagram: %s" % e) return contents
def _parse_datagram(self): self._address_regexp, index = osc_types.get_string(self._dgram, 0) if not self._dgram[index:]: # No params is legit, just return now. return # Get the parameters types. type_tag, index = osc_types.get_string(self._dgram, index) if type_tag.startswith(','): type_tag = type_tag[1:] # Parse each parameter given its type. for param in type_tag: if param == "i": # Integer. val, index = osc_types.get_int(self._dgram, index) elif param == "f": # Float. val, index = osc_types.get_float(self._dgram, index) elif param == "s": # String. val, index = osc_types.get_string(self._dgram, index) elif param == "b": # Blob. val, index = osc_types.get_blob(self._dgram, index) elif param == "T": # True. val = True elif param == "F": # False. val = False # TODO: Support more exotic types as described in the specification. else: logging.getLogger().warning('Unhandled parameter type: {0}'.format(param)) continue self._parameters.append(val)
def _parse_datagram(self): try: self._address_regexp, index = osc_types.get_string(self._dgram, 0) if not self._dgram[index:]: # No params is legit, just return now. return # Get the parameters types. type_tag, index = osc_types.get_string(self._dgram, index) if type_tag.startswith(','): type_tag = type_tag[1:] # Parse each parameter given its type. for param in type_tag: if param == "i": # Integer. val, index = osc_types.get_int(self._dgram, index) elif param == "f": # Float. val, index = osc_types.get_float(self._dgram, index) elif param == "s": # String. val, index = osc_types.get_string(self._dgram, index) elif param == "b": # Blob. val, index = osc_types.get_blob(self._dgram, index) elif param == "T": # True. val = True elif param == "F": # False. val = False # TODO: Support more exotic types as described in the specification. else: logging.warning( 'Unhandled parameter type: {0}'.format(param)) continue self._parameters.append(val) except osc_types.ParseError as pe: raise ParseError('Found incorrect datagram, ignoring it', pe)
def _parse_datagram(self): try: self._address_regexp, index = osc_types.get_string(self._dgram, 0) if not self._dgram[index:]: # No params is legit, just return now. return # Get the parameters types. type_tag, index = osc_types.get_string(self._dgram, index) if type_tag.startswith(','): type_tag = type_tag[1:] # Parse each parameter given its type. for param in type_tag: if param == "i": # Integer. val, index = osc_types.get_int(self._dgram, index) elif param == "f": # Float. val, index = osc_types.get_float(self._dgram, index) elif param == "s": # String. val, index = osc_types.get_string(self._dgram, index) elif param == "b": # Blob. val, index = osc_types.get_blob(self._dgram, index) # TODO: Support more exotic types as described in the specification. elif param == 0: # We've reached the end of the param string, finish now. return else: logging.warning('Unhandled parameter type: {0}'.format(param)) continue self._parameters.append(val) except osc_types.ParseError as pe: raise ParseError('Found incorrect datagram, ignoring it', pe)
def _parse_contents(self, index): contents = [] try: # An OSC Bundle Element consists of its size and its contents. # The size is an int32 representing the number of 8-bit bytes in the # contents, and will always be a multiple of 4. The contents are either # an OSC Message or an OSC Bundle. while self._dgram[index:]: # Get the sub content size. content_size, index = osc_types.get_int(self._dgram, index) # Get the datagram for the sub content. content_dgram = self._dgram[index:index + content_size] # Increment our position index up to the next possible content. index += content_size # Parse the content into an OSC message or bundle. if OscBundle.dgram_is_bundle(content_dgram): contents.append(OscBundle(content_dgram)) elif osc_message.OscMessage.dgram_is_message(content_dgram): contents.append(osc_message.OscMessage(content_dgram)) else: logging.warning( "Could not identify content type of dgram %s" % content_dgram) except (osc_types.ParseError, osc_message.ParseError, IndexError) as e: raise ParseError("Could not parse a content datagram: %s" % e) return contents
def load_file( cls, path: Union[str, bytes, os.PathLike]) -> Dict[float, List[OSCMessage]]: """Load a OSC file into a dict. Parameters ---------- path : Union[str, bytes, os.PathLike] Path of the OSC file. Returns ------- Dict[float, List[OSCMessage]] dict with time tag as keys and lists of OSCMessages as values. """ with open(path, "rb") as file: dgram = file.read() index = 0 builder = OscBundleBuilder(0) while dgram[index:]: size, index = osc_types.get_int(dgram, index) builder.add_content(OscBundle(dgram[index:index + size])) index += size # TODO rework that times don't get smashed warnings.warn( "This method currently destroys the time tag if they happend in the past." ) return convert_to_sc3nb_osc(builder.build()).messages()
def _parse_datagram(self): try: self._address_regexp, index = osc_types.get_string(self._dgram, 0) if not self._dgram[index:]: # No params is legit, just return now. return # Get the parameters types. type_tag, index = osc_types.get_string(self._dgram, index) if type_tag.startswith(','): type_tag = type_tag[1:] params = [] param_stack = [params] # Parse each parameter given its type. for param in type_tag: if param == "i": # Integer. val, index = osc_types.get_int(self._dgram, index) elif param == "f": # Float. val, index = osc_types.get_float(self._dgram, index) elif param == "d": # Double. val, index = osc_types.get_double(self._dgram, index) elif param == "s": # String. val, index = osc_types.get_string(self._dgram, index) elif param == "b": # Blob. val, index = osc_types.get_blob(self._dgram, index) elif param == "r": # RGBA. val, index = osc_types.get_rgba(self._dgram, index) elif param == "m": # MIDI. val, index = osc_types.get_midi(self._dgram, index) elif param == "t": # osc time tag: val, index = osc_types.get_ttag(self._dgram, index) elif param == "T": # True. val = True elif param == "F": # False. val = False elif param == "[": # Array start. array = [] param_stack[-1].append(array) param_stack.append(array) elif param == "]": # Array stop. if len(param_stack) < 2: raise ParseError('Unexpected closing bracket in type tag: {0}'.format(type_tag)) param_stack.pop() # TODO: Support more exotic types as described in the specification. else: logging.warning('Unhandled parameter type: {0}'.format(param)) continue if param not in "[]": param_stack[-1].append(val) if len(param_stack) != 1: raise ParseError('Missing closing bracket in type tag: {0}'.format(type_tag)) self._parameters = params except osc_types.ParseError as pe: raise ParseError('Found incorrect datagram, ignoring it', pe)
def __parse_sc_blob(data): '''Parses the blob from a SuperCollider osc message''' TYPE_TAG_MARKER = ord(b',') TYPE_TAG_START = 4 NUM_SIZE = 4 INT_TAG = ord(b'i') bytes2type = { ord(b'i'): lambda data: osc_types.get_int(data, 0), ord(b'f'): lambda data: osc_types.get_float(data, 0), ord(b's'): lambda data: osc_types.get_string(data, 0), } def __get_aligned_pos(pos): return NUM_SIZE * int(np.ceil((pos)/NUM_SIZE)) def __parse_list(data): list_size, _ = bytes2type[INT_TAG](data) type_tag_offset = __get_aligned_pos(list_size + 2) type_tag_end = TYPE_TAG_START + type_tag_offset type_tag = data[TYPE_TAG_START + 1: TYPE_TAG_START + 1 + list_size] value_list = [] idx = type_tag_end for t in type_tag: value, num_bytes = bytes2type[t](data[idx:]) value_list.append(value) idx += num_bytes return value_list, list_size def __parse_sc_msg(data): list_size, _ = bytes2type[INT_TAG](data) msg_size = __get_aligned_pos(NUM_SIZE + list_size) sc_list, _ = __parse_list(data[NUM_SIZE: msg_size]) return sc_list, msg_size bytes2type[ord(b'b')] = __parse_sc_msg def __parse_bundle(data): bundle_size = len(data) lists = [] idx = 16 # skip header while idx < bundle_size: sc_list, list_size = __parse_sc_msg(data[idx:]) lists.append(sc_list) idx += list_size return lists, bundle_size if data[TYPE_TAG_START] == TYPE_TAG_MARKER: return __parse_list(data)[0] elif data[:8] == b'#bundle\x00': return __parse_bundle(data)[0] else: return data
def test_get_integer(self): cases = { b"\x00\x00\x00\x00": (0, 4), b"\x00\x00\x00\x01": (1, 4), b"\x00\x00\x00\x02": (2, 4), b"\x00\x00\x00\x03": (3, 4), b"\x00\x00\x01\x00": (256, 4), b"\x00\x01\x00\x00": (65536, 4), b"\x01\x00\x00\x00": (16777216, 4), b"\x00\x00\x00\x01GARBAGE": (1, 4), } for dgram, expected in cases.items(): self.assertEqual(expected, osc_types.get_int(dgram, 0))
def _parse_list(dgram: bytes, start_index: int) -> Tuple[Sequence[Any], int]: """Parse a OSC List List consists of the following bytes: 4 bytes (int) : list_size n bytes (string) : OSC type tag n bytes (x) : content as specified by type tag Parameters ---------- dgram : bytes datagram with the list start_index : int parsing starting index Returns ------- Tuple[Sequence[Any], int] parsed list contents, starting index + number of consumed bytes Raises ------ ParseError If datagram is invalid. """ # parse list size _LOGGER.debug("[ start parsing list: %s", dgram[start_index:]) list_size, start_index = osc_types.get_int(dgram, start_index) # parse type tag type_tag, start_index = osc_types.get_string(dgram, start_index) if type_tag.startswith(","): type_tag = type_tag[1:] _LOGGER.debug("list with size %d and content '%s'", list_size, type_tag) # parse content value_list = [] for tag in type_tag: try: value, start_index = BYTES_2_TYPE[tag](dgram, start_index) except KeyError: raise ParseError('type tag "{}" not understood'.format(chr(tag))) _LOGGER.debug("new value %s", value) value_list.append(value) _LOGGER.debug("resulting list %s", value_list) _LOGGER.debug("] end parsing list") return value_list, start_index
def _parse_osc_bundle_element( dgram: bytes, start_index: int) -> Tuple[Union[Sequence[Any], bytes], int]: """Parse an element from an OSC bundle. The element needs to be either an OSC bundle or a list Parameters ---------- dgram : bytes datagram with the bundle element start_index : int parsing starting index Returns ------- Tuple[Union[Sequence[Any], bytes], int] parsed content of the bundle element, starting index + number of consumed bytes Raises ------ ParseError If the datagram is invalid. """ elem_size, start_index = osc_types.get_int(dgram, start_index) _LOGGER.debug( ">> parse OSC bundle element (size: %d): %s ", elem_size, dgram[start_index:start_index + elem_size], ) if OscBundle.dgram_is_bundle(dgram[start_index:start_index + elem_size]): _LOGGER.debug("found bundle") msgs, start_index = _parse_bundle(dgram, start_index) return msgs, start_index if dgram[start_index + TYPE_TAG_INDEX] == TYPE_TAG_MARKER: _LOGGER.debug("found list") value_list, start_index = _parse_list(dgram, start_index) return value_list, start_index if dgram[start_index:start_index + 4] == SYNTH_DEF_MARKER: _LOGGER.debug("found SynthDef blob") synth_def = dgram[start_index:start_index + elem_size] start_index = start_index + elem_size return synth_def, start_index raise ParseError("Datagram not recognized")
def test_get_integer(self): cases = { b"\x00\x00\x00\x00": (0, 4), b"\x00\x00\x00\x01": (1, 4), b"\x00\x00\x00\x02": (2, 4), b"\x00\x00\x00\x03": (3, 4), b"\x00\x00\x01\x00": (256, 4), b"\x00\x01\x00\x00": (65536, 4), b"\x01\x00\x00\x00": (16777216, 4), b"\x00\x00\x00\x01GARBAGE": (1, 4), } for dgram, expected in cases.items(): self.assertEqual( expected, osc_types.get_int(dgram, 0))
def parse_sclang_blob(data): '''Parses the blob from a SuperCollider osc message''' TYPE_TAG_MARKER = ord(b',') TYPE_TAG_START = 4 NUM_SIZE = 4 INT_TAG = ord(b'i') bytes2type = { ord(b'i'): lambda data: osc_types.get_int(data, 0), ord(b'f'): lambda data: osc_types.get_float(data, 0), ord(b's'): lambda data: osc_types.get_string(data, 0), ord(b'N'): lambda data: (None, 0), ord(b'I'): lambda data: (np.inf, 0), ord(b'T'): lambda data: (True, 0), ord(b'F'): lambda data: (False, 0) } def _get_aligned_pos(pos): return NUM_SIZE * int(np.ceil((pos) / NUM_SIZE)) def _parse_list(data): logging.debug("[ start parsing list: {}".format(data)) list_size, _ = bytes2type[INT_TAG](data) type_tag_offset = _get_aligned_pos(list_size + 2) type_tag_end = TYPE_TAG_START + type_tag_offset type_tag = data[TYPE_TAG_START + 1:TYPE_TAG_START + 1 + list_size] value_list = [] idx = type_tag_end for t in type_tag: try: value, num_bytes = bytes2type[t](data[idx:]) except KeyError: raise Exception('type tag "{}" not understood'.format(chr(t))) logging.debug("new value {}".format(value)) value_list.append(value) idx += num_bytes logging.debug("resulting list {}".format(value_list)) logging.debug("] end parsing list") return value_list, idx def _parse_sc_msg(data): logging.debug(">> parse sc msg: {}".format(data)) msg_size, _ = bytes2type[INT_TAG](data) logging.debug("msg size {}".format(msg_size)) data = data[NUM_SIZE:] if data[:8] == b'#bundle\x00': logging.debug("found bundle") msgs, bundle_size = _parse_bundle(data) return msgs, bundle_size + NUM_SIZE elif data[TYPE_TAG_START] == TYPE_TAG_MARKER: logging.debug("found list") value_list, list_size = _parse_list(data[:msg_size]) return value_list, list_size + NUM_SIZE else: raise Exception("Datagram not recognized") bytes2type[ord(b'b')] = _parse_sc_msg def _parse_bundle(data): logging.debug("## start parsing bundle: {}".format(data)) msgs = [] msg_count = ord(data[8 + 3:8 + 4]) - ord("\x80") bundle_size = 16 # skip header logging.debug("msg count {}".format(msg_count)) while msg_count > 0: sc_msg, msg_size = _parse_sc_msg(data[bundle_size:]) msgs.append(sc_msg) bundle_size += msg_size msg_count -= 1 logging.debug("msgs left {}".format(msg_count)) bundle_size = _get_aligned_pos(bundle_size) logging.debug("parsed bytes {}".format(data[:bundle_size])) logging.debug("msgs {}".format(msgs)) logging.debug("## end parsing bundle ") return msgs, bundle_size try: if len(data) > TYPE_TAG_START + 1: if data[TYPE_TAG_START] == TYPE_TAG_MARKER: return _parse_list(data)[0] elif data[:8] == b'#bundle\x00': return _parse_bundle(data)[0] except Exception as e: logging.warning('Ignoring Exception:\n{}\nreturning blob'.format(e)) return data