2525__version__ = "0.0.0+auto.0"
2626__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MIDI.git"
2727
28+ try :
29+ from typing import Union , Tuple , Any , List , Optional
30+ except ImportError :
31+ pass
32+
2833# From C3 - A and B are above G
2934# Semitones A B C D E F G
3035NOTE_OFFSET = [21 , 23 , 12 , 14 , 16 , 17 , 19 ]
3136
3237
33- def channel_filter (channel , channel_spec ):
38+ def channel_filter (
39+ channel : int , channel_spec : Optional [Union [int , Tuple [int , ...]]]
40+ ) -> bool :
3441 """
3542 Utility function to return True iff the given channel matches channel_spec.
3643 """
@@ -41,13 +48,12 @@ def channel_filter(channel, channel_spec):
4148 raise ValueError ("Incorrect type for channel_spec" + str (type (channel_spec )))
4249
4350
44- def note_parser (note ) :
51+ def note_parser (note : Union [ int , str ]) -> int :
4552 """If note is a string then it will be parsed and converted to a MIDI note (key) number, e.g.
4653 "C4" will return 60, "C#4" will return 61. If note is not a string it will simply be returned.
4754
4855 :param note: Either 0-127 int or a str representing the note, e.g. "C#4"
4956 """
50- midi_note = note
5157 if isinstance (note , str ):
5258 if len (note ) < 2 :
5359 raise ValueError ("Bad note format" )
@@ -61,7 +67,8 @@ def note_parser(note):
6167 sharpen = - 1
6268 # int may throw exception here
6369 midi_note = int (note [1 + abs (sharpen ) :]) * 12 + NOTE_OFFSET [noteidx ] + sharpen
64-
70+ elif isinstance (note , int ):
71+ midi_note = note
6572 return midi_note
6673
6774
@@ -82,40 +89,43 @@ class MIDIMessage:
8289 This is an *abstract* class.
8390 """
8491
85- _STATUS = None
92+ _STATUS : Optional [ int ] = None
8693 _STATUSMASK = None
87- LENGTH = None
94+ LENGTH : Optional [ int ] = None
8895 CHANNELMASK = 0x0F
8996 ENDSTATUS = None
9097
9198 # Commonly used exceptions to save memory
9299 @staticmethod
93- def _raise_valueerror_oor ():
100+ def _raise_valueerror_oor () -> None :
94101 raise ValueError ("Out of range" )
95102
96103 # Each element is ((status, mask), class)
97104 # order is more specific masks first
98- _statusandmask_to_class = []
105+ # Add better type hints for status, mask, class referenced above
106+ _statusandmask_to_class : List [
107+ Tuple [Tuple [Optional [bytes ], Optional [int ]], "MIDIMessage" ]
108+ ] = []
99109
100- def __init__ (self , * , channel = None ):
110+ def __init__ (self , * , channel : Optional [ int ] = None ) -> None :
101111 self ._channel = channel # dealing with pylint inadequacy
102112 self .channel = channel
103113
104114 @property
105- def channel (self ):
115+ def channel (self ) -> Optional [ int ] :
106116 """The channel number of the MIDI message where appropriate.
107117 This is *updated* by MIDI.send() method.
108118 """
109119 return self ._channel
110120
111121 @channel .setter
112- def channel (self , channel ) :
122+ def channel (self , channel : int ) -> None :
113123 if channel is not None and not 0 <= channel <= 15 :
114124 raise ValueError ("Channel must be 0-15 or None" )
115125 self ._channel = channel
116126
117127 @classmethod
118- def register_message_type (cls ):
128+ def register_message_type (cls ) -> None :
119129 """Register a new message by its status value and mask.
120130 This is called automagically at ``import`` time for each message.
121131 """
@@ -132,7 +142,14 @@ def register_message_type(cls):
132142
133143 # pylint: disable=too-many-arguments
134144 @classmethod
135- def _search_eom_status (cls , buf , eom_status , msgstartidx , msgendidxplusone , endidx ):
145+ def _search_eom_status (
146+ cls ,
147+ buf : bytearray ,
148+ eom_status : Optional [int ],
149+ msgstartidx : int ,
150+ msgendidxplusone : int ,
151+ endidx : int ,
152+ ) -> Tuple [int , bool , bool ]:
136153 good_termination = False
137154 bad_termination = False
138155
@@ -155,7 +172,9 @@ def _search_eom_status(cls, buf, eom_status, msgstartidx, msgendidxplusone, endi
155172 return (msgendidxplusone , good_termination , bad_termination )
156173
157174 @classmethod
158- def _match_message_status (cls , buf , msgstartidx , msgendidxplusone , endidx ):
175+ def _match_message_status (
176+ cls , buf : bytearray , msgstartidx : int , msgendidxplusone : int , endidx : int
177+ ) -> Tuple [Optional [Any ], int , bool , bool , bool , int ]:
159178 msgclass = None
160179 status = buf [msgstartidx ]
161180 known_msg = False
@@ -198,7 +217,9 @@ def _match_message_status(cls, buf, msgstartidx, msgendidxplusone, endidx):
198217
199218 # pylint: disable=too-many-locals,too-many-branches
200219 @classmethod
201- def from_message_bytes (cls , midibytes , channel_in ):
220+ def from_message_bytes (
221+ cls , midibytes : bytearray , channel_in : Optional [Union [int , Tuple [int , ...]]]
222+ ) -> Tuple [Optional ["MIDIMessage" ], int , int ]:
202223 """Create an appropriate object of the correct class for the
203224 first message found in some MIDI bytes filtered by channel_in.
204225
@@ -270,7 +291,7 @@ def from_message_bytes(cls, midibytes, channel_in):
270291
271292 # A default method for constructing wire messages with no data.
272293 # Returns an (immutable) bytes with just the status code in.
273- def __bytes__ (self ):
294+ def __bytes__ (self ) -> bytes :
274295 """Return the ``bytes`` wire protocol representation of the object
275296 with channel number applied where appropriate."""
276297 return bytes ([self ._STATUS ])
@@ -280,12 +301,12 @@ def __bytes__(self):
280301 # Returns the new object.
281302 # pylint: disable=unused-argument
282303 @classmethod
283- def from_bytes (cls , msg_bytes ) :
304+ def from_bytes (cls , msg_bytes : bytes ) -> "MIDIMessage" :
284305 """Creates an object from the byte stream of the wire protocol
285306 representation of the MIDI message."""
286307 return cls ()
287308
288- def __str__ (self ):
309+ def __str__ (self ) -> str :
289310 """Print an instance"""
290311 cls = self .__class__
291312 if slots := getattr (cls , "_message_slots" , None ):
@@ -313,7 +334,7 @@ class MIDIUnknownEvent(MIDIMessage):
313334 _message_slots = ["status" ]
314335 LENGTH = - 1
315336
316- def __init__ (self , status ):
337+ def __init__ (self , status : int ):
317338 self .status = status
318339 super ().__init__ ()
319340
@@ -333,7 +354,7 @@ class MIDIBadEvent(MIDIMessage):
333354
334355 _message_slots = ["msg_bytes" , "exception" ]
335356
336- def __init__ (self , msg_bytes , exception ):
357+ def __init__ (self , msg_bytes : bytearray , exception : Exception ):
337358 self .data = bytes (msg_bytes )
338359 self .exception_text = repr (exception )
339360 super ().__init__ ()
0 commit comments