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:
@@ -0,0 +1,366 @@
|
||||
"""
|
||||
Meta messages for MIDI files.
|
||||
|
||||
TODO:
|
||||
- what if an unknown meta message is implemented and someone depends on
|
||||
the 'data' attribute?
|
||||
- is 'type_byte' a good name?
|
||||
- 'values' is not a good name for a dictionary.
|
||||
- type and value safety?
|
||||
- copy().
|
||||
- expose _key_signature_encode/decode?
|
||||
"""
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from collections.abc import Generator, Iterable, Sequence
|
||||
from contextlib import contextmanager
|
||||
from numbers import Integral
|
||||
from typing import ClassVar, Literal, Never, Self, SupportsIndex, override, type_check_only
|
||||
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
from ..messages import BaseMessage
|
||||
|
||||
_charset: str = "latin1"
|
||||
|
||||
class KeySignatureError(Exception):
|
||||
"""Raised when key cannot be converted from key/mode to key letter"""
|
||||
|
||||
_key_signature_decode: dict[tuple[int, int], str]
|
||||
_key_signature_encode: dict[str, tuple[int, int]]
|
||||
_smpte_framerate_decode: dict[int, int | float]
|
||||
_smpte_framerate_encode: dict[int | float, int]
|
||||
|
||||
type _EncodeTypeNameSigned = Literal["byte", "short", "long"]
|
||||
type _EncodeTypeNameUnsigned = Literal["ubyte", "ushort", "ulong"]
|
||||
|
||||
def signed(to_type: _EncodeTypeNameSigned | _EncodeTypeNameUnsigned, n: int) -> int: ...
|
||||
def unsigned(to_type: _EncodeTypeNameSigned, n: int) -> int: ...
|
||||
def encode_variable_int(value: int) -> list[int]:
|
||||
"""Encode variable length integer.
|
||||
|
||||
Returns the integer as a list of bytes,
|
||||
where the last byte is < 128.
|
||||
|
||||
This is used for delta times and meta message payload
|
||||
length.
|
||||
"""
|
||||
|
||||
def decode_variable_int(value: Iterable[int]) -> int:
|
||||
"""Decode a list to a variable length integer.
|
||||
|
||||
Does the opposite of `encode_variable_int(value)`
|
||||
"""
|
||||
|
||||
def encode_string(string: str) -> list[int]: ...
|
||||
def decode_string(data: Iterable[SupportsIndex] | ReadableBuffer) -> str: ...
|
||||
@contextmanager
|
||||
def meta_charset(tmp_charset: str) -> Generator[None, Never]: ...
|
||||
def check_int(value: int | Integral, low: int | Integral, high: int | Integral) -> None: ...
|
||||
def check_str(value: str) -> None: ...
|
||||
|
||||
class MetaSpec(metaclass=ABCMeta): # not an ABC in runtime
|
||||
type: str
|
||||
@type_check_only
|
||||
@abstractmethod
|
||||
def decode(self, message: MetaMessage, data: Sequence[int]) -> None: ... # not exist in runtime
|
||||
@type_check_only
|
||||
@abstractmethod
|
||||
def encode(self, message: MetaMessage) -> list[int]: ... # not exist in runtime
|
||||
def check(self, name: Never, value: Never) -> None: ...
|
||||
|
||||
class MetaSpec_sequence_number(MetaSpec):
|
||||
type: str = "sequence_number"
|
||||
type_byte: int = 0x00
|
||||
attributes: ClassVar[list[str]] = ["number"]
|
||||
defaults: ClassVar[list[int]] = [0]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Sequence[int]) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
@override
|
||||
def check(self, name: Never, value: int | Integral) -> None: ...
|
||||
|
||||
class MetaSpec_text(MetaSpec):
|
||||
type: str = "text"
|
||||
type_byte: int = 0x01
|
||||
attributes: ClassVar[list[str]] = ["text"]
|
||||
defaults: ClassVar[list[str]] = [""]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Iterable[SupportsIndex] | ReadableBuffer) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
@override
|
||||
def check(self, name: Never, value: str) -> None: ...
|
||||
|
||||
class MetaSpec_copyright(MetaSpec_text):
|
||||
type: str = "copyright"
|
||||
type_byte: int = 0x02
|
||||
|
||||
class MetaSpec_track_name(MetaSpec_text):
|
||||
type: str = "track_name"
|
||||
type_byte: int = 0x03
|
||||
attributes: ClassVar[list[str]] = ["name"]
|
||||
defaults: ClassVar[list[str]] = [""]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Iterable[SupportsIndex] | ReadableBuffer) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
|
||||
class MetaSpec_instrument_name(MetaSpec_track_name):
|
||||
type: str = "instrument_name"
|
||||
type_byte: int = 0x04
|
||||
|
||||
class MetaSpec_lyrics(MetaSpec_text):
|
||||
type: str = "lyrics"
|
||||
type_byte: int = 0x05
|
||||
|
||||
class MetaSpec_marker(MetaSpec_text):
|
||||
type: str = "marker"
|
||||
type_byte: int = 0x06
|
||||
|
||||
class MetaSpec_cue_marker(MetaSpec_text):
|
||||
type: str = "cue_marker"
|
||||
type_byte: int = 0x07
|
||||
|
||||
class MetaSpec_device_name(MetaSpec_track_name):
|
||||
type: str = "device_name"
|
||||
type_byte: int = 0x09
|
||||
|
||||
class MetaSpec_channel_prefix(MetaSpec):
|
||||
type: str = "channel_prefix"
|
||||
type_byte: int = 0x20
|
||||
attributes: ClassVar[list[str]] = ["channel"]
|
||||
defaults: ClassVar[list[int]] = [0]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Sequence[int]) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
@override
|
||||
def check(self, name: Never, value: int) -> None: ...
|
||||
|
||||
class MetaSpec_midi_port(MetaSpec):
|
||||
type: str = "midi_port"
|
||||
type_byte: int = 0x21
|
||||
attributes: ClassVar[list[str]] = ["port"]
|
||||
defaults: ClassVar[list[int]] = [0]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Sequence[int]) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
@override
|
||||
def check(self, name: Never, value: int) -> None: ...
|
||||
|
||||
class MetaSpec_end_of_track(MetaSpec):
|
||||
type: str = "end_of_track"
|
||||
type_byte: int = 0x2F
|
||||
attributes: ClassVar[list[Never]] = []
|
||||
defaults: ClassVar[list[Never]] = []
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Sequence[int]) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
|
||||
class MetaSpec_set_tempo(MetaSpec):
|
||||
type: str = "set_tempo"
|
||||
type_byte: int = 0x51
|
||||
attributes: ClassVar[list[str]] = ["tempo"]
|
||||
defaults: ClassVar[list[int]] = [500000]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Sequence[int]) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
@override
|
||||
def check(self, name: Never, value: int) -> None: ...
|
||||
|
||||
class MetaSpec_smpte_offset(MetaSpec):
|
||||
type: str = "smpte_offset"
|
||||
type_byte: int = 0x54
|
||||
attributes: ClassVar[list[str]] = ["frame_rate", "hours", "minutes", "seconds", "frames", "sub_frames"]
|
||||
# TODO: What are some good defaults?
|
||||
defaults: ClassVar[list[int]] = [24, 0, 0, 0, 0, 0]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Sequence[int]) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
@override
|
||||
def check(self, name: str, value: int) -> None: ...
|
||||
|
||||
class MetaSpec_time_signature(MetaSpec):
|
||||
type: str = "time_signature"
|
||||
type_byte: int = 0x58
|
||||
# TODO: these need more sensible names.
|
||||
attributes: ClassVar[list[str]] = ["numerator", "denominator", "clocks_per_click", "notated_32nd_notes_per_beat"]
|
||||
defaults: ClassVar[list[int]] = [4, 4, 24, 8]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Sequence[int]) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
@override
|
||||
def check(self, name: str, value: int) -> None: ...
|
||||
|
||||
class MetaSpec_key_signature(MetaSpec):
|
||||
type: str = "key_signature"
|
||||
type_byte: int = 0x59
|
||||
attributes: ClassVar[list[str]] = ["key"]
|
||||
defaults: ClassVar[list[str]] = ["C"]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Sequence[int]) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
@override
|
||||
def check(self, name: Never, value: str) -> None: ...
|
||||
|
||||
class MetaSpec_sequencer_specific(MetaSpec):
|
||||
type: str = "sequencer_specific"
|
||||
type_byte: int = 0x7F
|
||||
attributes: ClassVar[list[str]] = ["data"]
|
||||
defaults: ClassVar[list[Sequence[int]]]
|
||||
@override
|
||||
def decode(self, message: MetaMessage, data: Iterable[int]) -> None: ...
|
||||
@override
|
||||
def encode(self, message: MetaMessage) -> list[int]: ...
|
||||
|
||||
def add_meta_spec(klass: type[MetaSpec]) -> None: ...
|
||||
|
||||
_META_SPECS: dict[int | str, type[MetaSpec]]
|
||||
_META_SPEC_BY_TYPE: dict[str, type[MetaSpec]]
|
||||
|
||||
def build_meta_message(
|
||||
meta_type: int | str, data: Sequence[int], delta: int = 0
|
||||
) -> MetaMessage | UnknownMetaMessage: ...
|
||||
|
||||
# NOTE: Generics not supported in runtime
|
||||
class _MetaMessage[T: str = str](BaseMessage):
|
||||
type: T
|
||||
time: int
|
||||
@property
|
||||
@override
|
||||
def is_meta(self) -> Literal[True]: ... # a normal attribute in runtime
|
||||
def __init__(self, type: str, skip_checks: bool = False, **kwargs: object) -> None: ...
|
||||
@override
|
||||
def copy(self, **overrides: object) -> Self:
|
||||
"""Return a copy of the message
|
||||
|
||||
Attributes will be overridden by the passed keyword arguments.
|
||||
Only message specific attributes can be overridden. The message
|
||||
type can not be changed.
|
||||
"""
|
||||
@override
|
||||
def __setattr__(self, name: str, value: object) -> None: ...
|
||||
@override
|
||||
def bytes(self) -> list[int]: ...
|
||||
@classmethod
|
||||
def from_bytes(cls, msg_bytes: Sequence[int]) -> MetaMessage | UnknownMetaMessage: ...
|
||||
|
||||
@type_check_only
|
||||
class SequenceNumberMetaMessage(_MetaMessage[Literal["sequence_number"]]):
|
||||
number: int
|
||||
|
||||
@type_check_only
|
||||
class TextMetaMessage(_MetaMessage[Literal["text"]]):
|
||||
text: str
|
||||
|
||||
@type_check_only
|
||||
class CopyrightMetaMessage(_MetaMessage[Literal["copyright"]]):
|
||||
text: str
|
||||
|
||||
@type_check_only
|
||||
class TrackNameMetaMessage(_MetaMessage[Literal["track_name"]]):
|
||||
name: str
|
||||
|
||||
@type_check_only
|
||||
class InstrumentNameMetaMessage(_MetaMessage[Literal["instrument_name"]]):
|
||||
name: str
|
||||
|
||||
@type_check_only
|
||||
class LyricsMetaMessage(_MetaMessage[Literal["lyrics"]]):
|
||||
text: str
|
||||
|
||||
@type_check_only
|
||||
class MarkerMetaMessage(_MetaMessage[Literal["marker"]]):
|
||||
text: str
|
||||
|
||||
@type_check_only
|
||||
class CueMarkerMetaMessage(_MetaMessage[Literal["cue_marker"]]):
|
||||
text: str
|
||||
|
||||
@type_check_only
|
||||
class DeviceNameMetaMessage(_MetaMessage[Literal["device_name"]]):
|
||||
name: str
|
||||
|
||||
@type_check_only
|
||||
class ChannelPrefixMetaMessage(_MetaMessage[Literal["channel_prefix"]]):
|
||||
channel: int
|
||||
|
||||
@type_check_only
|
||||
class MidiPortMetaMessage(_MetaMessage[Literal["midi_port"]]):
|
||||
port: int
|
||||
|
||||
@type_check_only
|
||||
class EndOfTrackMetaMessage(_MetaMessage[Literal["end_of_track"]]): ...
|
||||
|
||||
@type_check_only
|
||||
class SetTempoMetaMessage(_MetaMessage[Literal["set_tempo"]]):
|
||||
tempo: int
|
||||
|
||||
@type_check_only
|
||||
class SmpteOffsetMetaMessage(_MetaMessage[Literal["smpte_offset"]]):
|
||||
frame_rate: int
|
||||
hours: int
|
||||
minutes: int
|
||||
seconds: int
|
||||
frames: int
|
||||
sub_frames: int
|
||||
|
||||
@type_check_only
|
||||
class TimeSignatureMetaMessage(_MetaMessage[Literal["time_signature"]]):
|
||||
numerator: int
|
||||
denominator: int
|
||||
clocks_per_click: int
|
||||
notated_32nd_notes_per_beat: int
|
||||
|
||||
@type_check_only
|
||||
class KeySignatureMetaMessage(_MetaMessage[Literal["key_signature"]]):
|
||||
key: str
|
||||
|
||||
@type_check_only
|
||||
class SequencerSpecificMetaMessage(_MetaMessage[Literal["sequencer_specific"]]):
|
||||
data: Sequence[int]
|
||||
|
||||
class UnknownMetaMessage(_MetaMessage):
|
||||
type_byte: Sequence[int]
|
||||
data: tuple[int, ...]
|
||||
def __init__(
|
||||
self,
|
||||
type_byte: Sequence[int],
|
||||
data: Sequence[int] | None = ...,
|
||||
time: int = 0,
|
||||
type: str = "unknown_meta",
|
||||
**kwargs: Never,
|
||||
) -> None: ...
|
||||
@override
|
||||
def __setattr__(self, name: str, value: object) -> None: ...
|
||||
@override
|
||||
def bytes(self) -> list[int]: ...
|
||||
|
||||
# NOTE: `MetaMessage` is a concrete type (actually the `_MetaMessage` in this stub) in runtime
|
||||
type MetaMessage = (
|
||||
SequenceNumberMetaMessage
|
||||
| TextMetaMessage
|
||||
| CopyrightMetaMessage
|
||||
| TrackNameMetaMessage
|
||||
| InstrumentNameMetaMessage
|
||||
| LyricsMetaMessage
|
||||
| MarkerMetaMessage
|
||||
| CueMarkerMetaMessage
|
||||
| DeviceNameMetaMessage
|
||||
| ChannelPrefixMetaMessage
|
||||
| MidiPortMetaMessage
|
||||
| EndOfTrackMetaMessage
|
||||
| SetTempoMetaMessage
|
||||
| SmpteOffsetMetaMessage
|
||||
| TimeSignatureMetaMessage
|
||||
| KeySignatureMetaMessage
|
||||
| SequencerSpecificMetaMessage
|
||||
)
|
||||
Reference in New Issue
Block a user