@@ -131,6 +131,75 @@ def register_message_type(cls):
131131 MIDIMessage ._statusandmask_to_class .insert (insert_idx ,
132132 ((cls ._STATUS , cls ._STATUSMASK ), cls ))
133133
134+
135+ # pylint: disable=too-many-arguments
136+ @classmethod
137+ def _search_eom_status (cls , buf , eom_status , msgstartidx , msgendidxplusone , endidx ):
138+ good_termination = False
139+ bad_termination = False
140+
141+ msgendidxplusone = msgstartidx + 1
142+ while msgendidxplusone <= endidx :
143+ # Look for a status byte
144+ # Second rule of the MIDI club is status bytes have MSB set
145+ if buf [msgendidxplusone ] & 0x80 :
146+ if buf [msgendidxplusone ] == eom_status :
147+ good_termination = True
148+ else :
149+ bad_termination = True
150+ break
151+ else :
152+ msgendidxplusone += 1
153+
154+ if good_termination or bad_termination :
155+ msgendidxplusone += 1
156+
157+ return (msgendidxplusone , good_termination , bad_termination )
158+
159+ # pylint: disable=too-many-arguments,too-many-locals
160+ @classmethod
161+ def _match_message_status (cls , buf , channel_in , msgstartidx , msgendidxplusone , endidx ):
162+ msgclass = None
163+ status = buf [msgstartidx ]
164+ known_msg = False
165+ complete_msg = False
166+ bad_termination = False
167+ channel_match_orna = True
168+ channel = None
169+
170+ # Rummage through our list looking for a status match
171+ for status_mask , msgclass in MIDIMessage ._statusandmask_to_class :
172+ masked_status = status & status_mask [1 ]
173+ if status_mask [0 ] == masked_status :
174+ known_msg = True
175+ # Check there's enough left to parse a complete message
176+ # this value can be changed later for a var. length msgs
177+ complete_msg = len (buf ) - msgstartidx >= msgclass .LENGTH
178+ if not complete_msg :
179+ break
180+
181+ if msgclass .CHANNELMASK is not None :
182+ channel = status & msgclass .CHANNELMASK
183+ channel_match_orna = channel_filter (channel , channel_in )
184+
185+ if msgclass .LENGTH < 0 : # indicator of variable length message
186+ (msgendidxplusone ,
187+ terminated_msg ,
188+ bad_termination ) = cls ._search_eom_status (buf ,
189+ msgclass .ENDSTATUS ,
190+ msgstartidx ,
191+ msgendidxplusone ,
192+ endidx )
193+ if not terminated_msg :
194+ complete_msg = False
195+ else : # fixed length message
196+ msgendidxplusone = msgstartidx + msgclass .LENGTH
197+ break
198+
199+ return (msgclass , status ,
200+ known_msg , complete_msg , bad_termination ,
201+ channel_match_orna , channel , msgendidxplusone )
202+
134203 @classmethod
135204 def from_message_bytes (cls , midibytes , channel_in ):
136205 """Create an appropriate object of the correct class for the
@@ -140,11 +209,11 @@ def from_message_bytes(cls, midibytes, channel_in):
140209 or for no messages, partial messages or messages for other channels
141210 (None, endplusone, skipped, None).
142211 """
143-
144212 msg = None
145213 endidx = len (midibytes ) - 1
146214 skipped = 0
147215 preamble = True
216+ channel = None
148217
149218 msgstartidx = 0
150219 msgendidxplusone = 0
@@ -155,66 +224,39 @@ def from_message_bytes(cls, midibytes, channel_in):
155224 msgstartidx += 1
156225 if preamble :
157226 skipped += 1
158-
159227 preamble = False
160228
161229 # Either no message or a partial one
162230 if msgstartidx > endidx :
163231 return (None , endidx + 1 , skipped , None )
164232
165- status = midibytes [msgstartidx ]
166- known_message = False
167- complete_message = False
168- channel_match_orna = True
169- channel = None
170- # Rummage through our list looking for a status match
171- for status_mask , msgclass in MIDIMessage ._statusandmask_to_class :
172- masked_status = status & status_mask [1 ]
173- if status_mask [0 ] == masked_status :
174- known_message = True
175- # Check there's enough left to parse a complete message
176- # this value can be changed later for a var. length msgs
177- complete_message = len (midibytes ) - msgstartidx >= msgclass .LENGTH
178- if complete_message :
179- if msgclass .CHANNELMASK is not None :
180- channel = status & msgclass .CHANNELMASK
181- channel_match_orna = channel_filter (channel , channel_in )
182-
183- bad_termination = False
184- if msgclass .LENGTH < 0 : # indicator of variable length message
185- terminated_message = False
186- msgendidxplusone = msgstartidx + 1
187- while msgendidxplusone <= endidx :
188- if midibytes [msgendidxplusone ] & 0x80 :
189- if midibytes [msgendidxplusone ] == msgclass .ENDSTATUS :
190- terminated_message = True
191- else :
192- bad_termination = True
193- break
194- else :
195- msgendidxplusone += 1
196- if terminated_message or bad_termination :
197- msgendidxplusone += 1
198- if not terminated_message :
199- complete_message = False
200- else :
201- msgendidxplusone = msgstartidx + msgclass .LENGTH
202-
203- if complete_message and not bad_termination and channel_match_orna :
204- try :
205- msg = msgclass .from_bytes (midibytes [msgstartidx + 1 :msgendidxplusone ])
206- except (ValueError , TypeError ) as ex :
207- msg = MIDIBadEvent (midibytes [msgstartidx + 1 :msgendidxplusone ], ex )
208-
209- break # for
233+ # Try and match the status byte found in midibytes
234+ (msgclass ,
235+ status ,
236+ known_message ,
237+ complete_message ,
238+ bad_termination ,
239+ channel_match_orna ,
240+ channel ,
241+ msgendidxplusone ) = cls ._match_message_status (midibytes ,
242+ channel_in ,
243+ msgstartidx ,
244+ msgendidxplusone ,
245+ endidx )
246+
247+ if complete_message and not bad_termination and channel_match_orna :
248+ try :
249+ msg = msgclass .from_bytes (midibytes [msgstartidx + 1 :msgendidxplusone ])
250+ except (ValueError , TypeError ) as ex :
251+ msg = MIDIBadEvent (midibytes [msgstartidx + 1 :msgendidxplusone ], ex )
210252
211253 # break out of while loop for a complete message on good channel
212254 # or we have one we do not know about
213255 if known_message :
214256 if complete_message :
215257 if channel_match_orna :
216258 break
217- else :
259+ else : # advance to next message
218260 msgstartidx = msgendidxplusone
219261 else :
220262 # Important case of a known message but one that is not
@@ -227,10 +269,7 @@ def from_message_bytes(cls, midibytes, channel_in):
227269 msgendidxplusone = msgstartidx + 1
228270 break
229271
230- if msg is not None :
231- return (msg , msgendidxplusone , skipped , channel )
232- else :
233- return (None , msgendidxplusone , skipped , None )
272+ return (msg , msgendidxplusone , skipped , channel )
234273
235274 # channel value present to keep interface uniform but unused
236275 # pylint: disable=unused-argument
0 commit comments