180 lines
4.8 KiB
Python
180 lines
4.8 KiB
Python
|
|
"""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()`.
|
||
|
|
"""
|