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