@@ -88,80 +88,76 @@ def __init__(self, data: bytes):
8888 data_start = align_next_word (data_start )
8989 i = type_start + 1
9090 d = data_start
91- while data [i ] != 0x00 :
92- t = chr (data [i ])
93- if t == "i" :
94- types .append (int )
95- n = (data [d ] << 24 ) | (data [d + 1 ] << 16 ) | (data [d + 2 ] << 8 ) | data [d + 1 ]
96- self .values .append (n )
97- d += 4
98- elif t == "f" :
99- types .append (float )
100- n = struct .unpack (">f" , data [d : d + 4 ])[0 ]
101- self .values .append (n )
102- d += 4
103- elif t == "s" or t == "S" : # include the alternate "S" here
104- types .append (str )
105- s = ""
106- string_end = data .index (b"\0 " , d )
107- for c in range (d , string_end ):
108- s += chr (data [c ])
109- self .values .append (s )
110- d = string_end
111- d = align_next_word (d )
112- elif t == "b" :
113- # blob; int32 -> n, followed by n bytes
114- types .append (bytearray )
115- n = (data [d ] << 24 ) | (data [d + 1 ] << 16 ) | (data [d + 2 ] << 8 ) | data [d + 1 ]
116- d += 4
117- b = []
118- for i in range (n ):
119- b .append (data [d + i ])
120- d += n
121- d = align_next_word (d )
122- self .values .append (bytearray (b ))
123- elif t == "T" or t == "F" :
124- # zero-byte boolean; skip
125- pass
126- elif t == "t" :
127- # 8-byte timestamp; skip
128- d += 8
129- elif t == "h" :
130- # 64-bit signed integer
131- # treat as a normal int
132- types .append (int )
133- n = (
134- (data [d ] << 56 )
135- | (data [d + 1 ] << 48 )
136- | (data [d + 2 ] << 40 )
137- | (data [d + 3 ] << 32 )
138- | (data [d + 4 ] << 24 )
139- | (data [d + 5 ] << 16 )
140- | (data [d + 6 ] << 8 )
141- | data [d + 7 ]
142- )
143- self .values .append (n )
144- d += 8
145- elif t == "c" :
146- # a single character; treat as a string
147- types .append (str )
148- self .values .append (
149- data [d + 3 ].decode () # data is in the 4th byte; padded with leading zeros
150- )
151- d += 4
152- elif t == "m" :
153- # 4-byte midi; skip
154- d += 4
155- elif t == "N" :
156- # nil; skip
157- pass
158- elif t == "I" :
159- # infinity; skip
160- pass
161- else :
162- log_warning (f"Unsupported type { t } " , "osc" )
91+ try :
92+ while i < len (data ) and data [i ] != 0x00 :
93+ t = chr (data [i ])
94+ if t == "i" :
95+ types .append (int )
96+ n = int .from_bytes (data [d : d + 4 ], "big" )
97+ self .values .append (n )
98+ d += 4
99+ elif t == "f" :
100+ types .append (float )
101+ n = struct .unpack (">f" , data [d : d + 4 ])[0 ]
102+ self .values .append (n )
103+ d += 4
104+ elif t == "s" or t == "S" : # include the alternate "S" here
105+ types .append (str )
106+ s = ""
107+ string_end = data .index (b"\0 " , d )
108+ for c in range (d , string_end ):
109+ s += chr (data [c ])
110+ self .values .append (s )
111+ d = string_end
112+ d = align_next_word (d )
113+ elif t == "b" :
114+ # blob; int32 -> n, followed by n bytes
115+ types .append (bytearray )
116+ n = int .from_bytes (data [d : d + 4 ], "big" )
117+ d += 4
118+ b = []
119+ for j in range (n ):
120+ b .append (data [d + j ])
121+ d += n
122+ d = align_next_word (d )
123+ self .values .append (bytearray (b ))
124+ elif t == "T" or t == "F" :
125+ # zero-byte boolean; skip
126+ pass
127+ elif t == "t" :
128+ # 8-byte timestamp; skip
129+ d += 8
130+ elif t == "h" :
131+ # 64-bit signed integer
132+ # treat as a normal int
133+ types .append (int )
134+ n = int .from_bytes (data [d : d + 8 ], "big" )
135+ self .values .append (n )
136+ d += 8
137+ elif t == "c" :
138+ # a single character; treat as a string
139+ types .append (str )
140+ self .values .append (
141+ data [d + 3 ].decode () # data is in the 4th byte; padded with leading zeros
142+ )
143+ d += 4
144+ elif t == "m" :
145+ # 4-byte midi; skip
146+ d += 4
147+ elif t == "N" :
148+ # nil; skip
149+ pass
150+ elif t == "I" :
151+ # infinity; skip
152+ pass
153+ else :
154+ log_warning (f"Unsupported type { t } " , "osc" )
163155
164- i += 1
156+ i += 1
157+ except IndexError :
158+ # If we fall off of the array for any reason, just keep what we have so far.
159+ # This could happen if the data got corrupted in transport.
160+ pass
165161
166162 @property
167163 def values (self ) -> list [int | float | str | bytearray ]:
@@ -177,6 +173,9 @@ def address(self) -> str:
177173 """This packet's address"""
178174 return self ._address
179175
176+ def __str__ (self ):
177+ return f"{ self .address } : { self .values } "
178+
180179
181180class OpenSoundServer :
182181 """
@@ -250,13 +249,47 @@ def wrapper(*args, **kwargs):
250249 self .recv_callback = wrapper
251250 return wrapper
252251
252+ def parse_packets (self , data , result ):
253+ """
254+ Recursively process the raw data, decomposing bundles into an array of packets.
255+
256+ :param[in] data: The raw byte data received over the socket
257+ :param[out] result: An array we can recusively append bundle data to
258+ """
259+ if len (data ) > 8 and data [0 :7 ].decode ("utf-8" ) == "#bundle" :
260+ # We're processing a bundle
261+ # The first 8 bytes after the header are the timestamp, which we don't support
262+ # so skip that and go straight to the payload
263+ # ['#', 'b', 'u', 'n', 'd', l', 'e', '\0', t0, t1, t2, t3, t4, t5, t6, t7]
264+ try :
265+ data = data [16 :]
266+ while len (data ) > 0 :
267+ element_length = int .from_bytes (data [0 :4 ], "big" )
268+ data = data [4 :]
269+ self .parse_packets (data , result )
270+ data = data [element_length :]
271+ except IndexError as err :
272+ # either the length got corrupted, or the packet was partially dropped
273+ # either way, just process what we can and move on
274+ pass
275+ else :
276+ # we're processing a normal packet; add it to the result
277+ packet = OpenSoundPacket (data )
278+ result .append (packet )
279+
280+ @property
281+ def elements (self ):
282+ return self ._elements
283+
253284 def receive_data (self ):
254285 """Check if we have any new data to process, invoke data_handler as needed"""
255286 while True :
256287 try :
257288 (data , connection ) = self .recv_socket .recvfrom (1024 )
258- packet = OpenSoundPacket (data )
259- self .recv_callback (connection = connection , data = packet )
289+ packets = []
290+ self .parse_packets (data , packets )
291+ for packet in packets :
292+ self .recv_callback (connection = connection , data = packet )
260293 except ValueError as err :
261294 log_warning (f"Failed to process packet: { err } " , "osc" )
262295 break
0 commit comments