Files
termidi/typings/mido/midifiles/meta.pyi
T
NCBM 9009a7c5bc 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.
2026-04-26 00:51:40 +08:00

367 lines
12 KiB
Python

"""
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
)