Skip to content

Commit 0ae13ef

Browse files
author
Kevin J Walters
committed
Chopping up the unwieldy code in MIDIMessage.from_message_bytes(), some code has gone into two new class methods, _match_message_status() and _search_eom_status(). Also simplified the final condition around return. #3
1 parent 6e1257e commit 0ae13ef

1 file changed

Lines changed: 91 additions & 52 deletions

File tree

adafruit_midi/midi_message.py

Lines changed: 91 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)