Add type stubs for mido MIDI library

- Created type stubs for various modules in the mido library including messages, midifiles, parser, ports, sockets, syx, tokenizer, and version.
- Implemented type hints for functions and classes to improve type checking and code clarity.
- Added support for MIDI over TCP/IP in sockets module.
- Included methods for reading and writing SYX files in syx module.
- Enhanced the parser functionality with a dedicated Parser class for MIDI byte streams.
- Established a structure for MIDI file handling with MidiFile and MidiTrack classes.
This commit is contained in:
2026-04-26 00:51:40 +08:00
parent e5c692e5d7
commit 9009a7c5bc
23 changed files with 1875 additions and 0 deletions
+179
View File
@@ -0,0 +1,179 @@
"""Definitions and lookup tables for MIDI messages.
TODO:
* add lookup functions for messages definitions by type and status
byte.
"""
from collections.abc import Sequence
from typing import Literal, NotRequired, TypedDict, final, overload, type_check_only
CHANNEL_MESSAGES: set[int]
COMMON_MESSAGES: set[int]
REALTIME_MESSAGES: set[int]
SYSEX_START: int = 0xF0
SYSEX_END: int = 0xF7
# Pitchwheel is a 14 bit signed integer
MIN_PITCHWHEEL: int = -8192
MAX_PITCHWHEEL: int = 8191
# Song pos is a 14 bit unsigned integer
MIN_SONGPOS: int = 0
MAX_SONGPOS: int = 16383
@type_check_only
class SpecsDict(TypedDict):
status_byte: int
type: str
value_names: tuple[str, ...]
attribute_names: set[str]
length: int | float
@type_check_only
class MsgDictOverride(TypedDict):
channel: NotRequired[int]
control: NotRequired[int]
data: NotRequired[Sequence[int]]
frame_type: NotRequired[int]
frame_value: NotRequired[int]
note: NotRequired[int]
pitch: NotRequired[int]
pos: NotRequired[int]
program: NotRequired[int]
song: NotRequired[int]
value: NotRequired[int]
velocity: NotRequired[int]
time: NotRequired[int | float]
@type_check_only
class MsgDictBase[T: str](TypedDict):
type: T
time: int | float
@type_check_only
class ChannelMsgDictBase[
T: Literal["note_off", "note_on", "polytouch", "control_change", "program_change", "aftertouch", "pitchwheel"]
](MsgDictBase[T]):
channel: int
@type_check_only
@final
class NoteMsgDict(ChannelMsgDictBase[Literal["note_off", "note_on"]]):
note: int
velocity: int
@type_check_only
@final
class PolytouchMsgDict(ChannelMsgDictBase[Literal["polytouch"]]):
note: int
value: int
@type_check_only
@final
class ControlChangeMsgDict(ChannelMsgDictBase[Literal["control_change"]]):
control: int
value: int
@type_check_only
@final
class ProgramChangeMsgDict(ChannelMsgDictBase[Literal["program_change"]]):
program: int
@type_check_only
@final
class AftertouchMsgDict(ChannelMsgDictBase[Literal["aftertouch"]]):
value: int
@type_check_only
@final
class PitchwheelMsgDict(ChannelMsgDictBase[Literal["pitchwheel"]]):
pitch: int
@type_check_only
@final
class SysExMsgDict(MsgDictBase[Literal["sysex"]]):
data: Sequence[int]
@type_check_only
@final
class QuarterFrameMsgDict(MsgDictBase[Literal["quarter_frame"]]):
frame_type: int
frame_value: int
@type_check_only
@final
class SongposMsgDict(MsgDictBase[Literal["songpos"]]):
pos: int
@type_check_only
@final
class SongSelectMsgDict(MsgDictBase[Literal["song_select"]]):
song: int
@type_check_only
@final
class RealTimeMsgDict(
MsgDictBase[Literal["tune_request", "clock", "start", "continue", "stop", "active_sensing", "reset"]]
): ...
type MsgDict = (
NoteMsgDict
| PolytouchMsgDict
| ControlChangeMsgDict
| ProgramChangeMsgDict
| AftertouchMsgDict
| PitchwheelMsgDict
| SysExMsgDict
| QuarterFrameMsgDict
| SongposMsgDict
| SongSelectMsgDict
| RealTimeMsgDict
)
SPECS: list[SpecsDict]
SPEC_LOOKUP: dict[str | int, SpecsDict]
SPEC_BY_STATUS: dict[int, SpecsDict]
SPEC_BY_TYPE: dict[str, SpecsDict]
REALTIME_TYPES: set[str]
DEFAULT_VALUES: MsgDict
@overload
def make_msgdict(type_: Literal["note_off", "note_on"], overrides: MsgDictOverride) -> NoteMsgDict: ...
@overload
def make_msgdict(type_: Literal["polytouch"], overrides: MsgDictOverride) -> PolytouchMsgDict: ...
@overload
def make_msgdict(type_: Literal["control_change"], overrides: MsgDictOverride) -> ControlChangeMsgDict: ...
@overload
def make_msgdict(type_: Literal["program_change"], overrides: MsgDictOverride) -> ProgramChangeMsgDict: ...
@overload
def make_msgdict(type_: Literal["aftertouch"], overrides: MsgDictOverride) -> AftertouchMsgDict: ...
@overload
def make_msgdict(type_: Literal["pitchwheel"], overrides: MsgDictOverride) -> PitchwheelMsgDict: ...
@overload
def make_msgdict(type_: Literal["sysex"], overrides: MsgDictOverride) -> SysExMsgDict: ...
@overload
def make_msgdict(type_: Literal["quarter_frame"], overrides: MsgDictOverride) -> QuarterFrameMsgDict: ...
@overload
def make_msgdict(type_: Literal["songpos"], overrides: MsgDictOverride) -> SongposMsgDict: ...
@overload
def make_msgdict(type_: Literal["song_select"], overrides: MsgDictOverride) -> SongSelectMsgDict: ...
@overload
def make_msgdict(
type_: Literal["tune_request", "clock", "start", "continue", "stop", "active_sensing", "reset"],
overrides: MsgDictOverride,
) -> RealTimeMsgDict: ...
@overload
def make_msgdict[T: str](type_: T, overrides: MsgDictOverride) -> MsgDictBase[T]: ...
def make_msgdict[T: str](type_: T, overrides: MsgDictOverride) -> MsgDictBase[T]:
"""Return a new message.
Returns a dictionary representing a message.
Message values can be overriden.
No type or value checking is done. The caller is responsible for
calling `check_msgdict()`.
"""