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,21 @@
|
||||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from .meta import KeySignatureError, MetaMessage, UnknownMetaMessage
|
||||
from .midifiles import MidiFile
|
||||
from .tracks import MidiTrack, merge_tracks
|
||||
from .units import bpm2tempo, second2tick, tempo2bpm, tick2second
|
||||
|
||||
__all__ = [
|
||||
"KeySignatureError",
|
||||
"MetaMessage",
|
||||
"MidiFile",
|
||||
"MidiTrack",
|
||||
"UnknownMetaMessage",
|
||||
"bpm2tempo",
|
||||
"merge_tracks",
|
||||
"second2tick",
|
||||
"tempo2bpm",
|
||||
"tick2second",
|
||||
]
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1,154 @@
|
||||
"""
|
||||
MIDI file reading and playback.
|
||||
|
||||
References:
|
||||
|
||||
http://home.roadrunner.com/~jgglatt/
|
||||
http://home.roadrunner.com/~jgglatt/tech/miditech.htm
|
||||
http://home.roadrunner.com/~jgglatt/tech/midifile.htm
|
||||
|
||||
http://www.sonicspot.com/guide/midifiles.html
|
||||
http://www.ccarh.org/courses/253/assignment/midifile/
|
||||
https://code.google.com/p/binasc/wiki/mainpage
|
||||
http://stackoverflow.com/questions/2984608/midi-delta-time
|
||||
http://www.recordingblogs.com/sa/tabid/82/EntryId/44/MIDI-Part-XIII-Delta-time-a
|
||||
http://www.sonicspot.com/guide/midifiles.html
|
||||
"""
|
||||
|
||||
from collections.abc import Callable, Generator, Iterable, Sequence
|
||||
from numbers import Real
|
||||
from typing import IO, Literal, Never, Self, overload
|
||||
|
||||
from ..messages import BaseMessage, Message
|
||||
from .meta import MetaMessage, UnknownMetaMessage
|
||||
from .tracks import MidiTrack
|
||||
|
||||
type _RealNumber = int | float | Real
|
||||
|
||||
# The default tempo is 120 BPM.
|
||||
# (500000 microseconds per beat (quarter note).)
|
||||
DEFAULT_TEMPO: int = 500000
|
||||
DEFAULT_TICKS_PER_BEAT: int = 480
|
||||
|
||||
# Maximum message length to attempt to read.
|
||||
MAX_MESSAGE_LENGTH: int = 1000000
|
||||
|
||||
def print_byte(byte: int, pos: int = 0) -> None: ...
|
||||
|
||||
class DebugFileWrapper:
|
||||
def __init__(self, file: IO[bytes]) -> None: ...
|
||||
def read(self, size: int) -> bytes: ...
|
||||
def tell(self) -> int: ...
|
||||
|
||||
def read_byte(self: IO[bytes]) -> int: ...
|
||||
def read_bytes(infile: IO[bytes], size: int) -> list[int]: ...
|
||||
def read_chunk_header(infile: IO[bytes]) -> tuple[str, int]: ...
|
||||
def read_file_header(infile: IO[bytes]) -> tuple[int, int, int]: ...
|
||||
def read_message(
|
||||
infile: IO[bytes], status_byte: int, peek_data: Sequence[int], delta: int, clip: bool = False
|
||||
) -> Message: ...
|
||||
def read_sysex(infile: IO[bytes], delta: int, clip: bool = False) -> Message: ...
|
||||
def read_variable_int(infile: IO[bytes]) -> int: ...
|
||||
def read_meta_message(infile: IO[bytes], delta: int) -> MetaMessage | UnknownMetaMessage: ...
|
||||
def read_track(infile: IO[bytes], debug: bool = False, clip: bool = False) -> MidiTrack: ...
|
||||
def write_chunk(outfile: IO[bytes], name: bytes, data: bytes) -> None:
|
||||
"""Write an IFF chunk to the file.
|
||||
|
||||
`name` must be a bytestring."""
|
||||
|
||||
def write_track(outfile: IO[bytes], track: Iterable[BaseMessage]) -> None: ...
|
||||
def get_seconds_per_tick(tempo: _RealNumber, ticks_per_beat: _RealNumber) -> float: ...
|
||||
|
||||
class MidiFile:
|
||||
filename: str | None
|
||||
type: Literal[0, 1, 2]
|
||||
ticks_per_beat: int
|
||||
charset: str
|
||||
debug: bool
|
||||
clip: bool
|
||||
tracks: list[MidiTrack]
|
||||
def __init__(
|
||||
self,
|
||||
filename: str | None = ...,
|
||||
file: IO[bytes] | None = ...,
|
||||
type: Literal[0, 1, 2] = 1,
|
||||
ticks_per_beat: int = DEFAULT_TICKS_PER_BEAT, # noqa: PYI011
|
||||
charset: str = "latin1",
|
||||
debug: bool = False,
|
||||
clip: bool = False,
|
||||
tracks: list[MidiTrack] | None = ...,
|
||||
) -> None: ...
|
||||
@property
|
||||
def merged_track(self) -> MidiTrack: ...
|
||||
@merged_track.deleter
|
||||
def merged_track(self) -> None: ...
|
||||
def add_track(self, name: str | None = ...) -> MidiTrack:
|
||||
"""Add a new track to the file.
|
||||
|
||||
This will create a new `MidiTrack` object and append it to the
|
||||
track list.
|
||||
"""
|
||||
|
||||
@property
|
||||
def length(self) -> int | float:
|
||||
"""Playback time in seconds.
|
||||
|
||||
This will be computed by going through every message in every
|
||||
track and adding up delta times.
|
||||
"""
|
||||
|
||||
def __iter__(self) -> Generator[Message | MetaMessage | UnknownMetaMessage, Never]: ...
|
||||
@overload
|
||||
def play(
|
||||
self, meta_messages: Literal[False] = False, now: Callable[[], float] = ...
|
||||
) -> Generator[Message, Never]: ...
|
||||
@overload
|
||||
def play(
|
||||
self, meta_messages: bool, now: Callable[[], float] = ...
|
||||
) -> Generator[Message | MetaMessage | UnknownMetaMessage, Never]: ...
|
||||
def play(
|
||||
self, meta_messages: bool = False, now: Callable[[], float] = ...
|
||||
) -> Generator[Message | MetaMessage | UnknownMetaMessage, Never]:
|
||||
"""Play back all tracks.
|
||||
|
||||
The generator will sleep between each message by
|
||||
default. Messages are yielded with correct timing. The time
|
||||
attribute is set to the number of seconds slept since the
|
||||
previous message.
|
||||
|
||||
By default you will only get normal MIDI messages. Pass
|
||||
`meta_messages=True` if you also want meta messages.
|
||||
|
||||
You will receive copies of the original messages, so you can
|
||||
safely modify them without ruining the tracks.
|
||||
|
||||
By default the system clock is used for the timing of yielded
|
||||
MIDI events. To use a different clock (e.g. to synchronize to
|
||||
an audio stream), pass `now=time_fn` where `time_fn` is a zero
|
||||
argument function that yields the current time in seconds.
|
||||
"""
|
||||
|
||||
def save(self, filename: str | None = ..., file: IO[bytes] | None = ...) -> None:
|
||||
"""Save to a file.
|
||||
|
||||
If file is passed the data will be saved to that file. This is
|
||||
typically an in-memory file or and already open file like `sys.stdout`.
|
||||
|
||||
If filename is passed the data will be saved to that file.
|
||||
|
||||
Raises `ValueError` if both file and filename are None,
|
||||
or if a type 0 file has != one track.
|
||||
"""
|
||||
|
||||
def print_tracks(self, meta_only: bool = False) -> None:
|
||||
"""Prints out all messages in a .midi file.
|
||||
|
||||
May take argument `meta_only` to show only meta messages.
|
||||
|
||||
Use:
|
||||
`print_tracks()` -> will print all messages
|
||||
`print_tracks(meta_only=True)` -> will print only MetaMessages
|
||||
"""
|
||||
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(self, type: object, value: object, traceback: object) -> Literal[False]: ...
|
||||
@@ -0,0 +1,46 @@
|
||||
from collections.abc import Generator, Iterable, Sequence
|
||||
from typing import Never, Self, SupportsIndex, overload, override
|
||||
|
||||
from ..messages import BaseMessage
|
||||
|
||||
class MidiTrack(list[BaseMessage]):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Name of the track.
|
||||
|
||||
This will return the name from the first `track_name` meta
|
||||
message in the track, or `''` if there is no such message.
|
||||
|
||||
Setting this property will update the name field of the first
|
||||
`track_name` message in the track. If no such message is found,
|
||||
one will be added to the beginning of the track with a delta
|
||||
time of 0."""
|
||||
|
||||
@name.setter
|
||||
def name(self, name: str) -> None: ...
|
||||
@override
|
||||
def copy(self) -> Self: ...
|
||||
@overload
|
||||
def __getitem__(self, index_or_slice: SupportsIndex) -> BaseMessage: ...
|
||||
@overload
|
||||
def __getitem__(self, index_or_slice: slice[int, int, int]) -> Self: ...
|
||||
@override
|
||||
def __add__(self, other: Sequence[BaseMessage]) -> Self: ... # pyright: ignore[reportIncompatibleMethodOverride]
|
||||
@override
|
||||
def __mul__(self, other: SupportsIndex) -> Self: ...
|
||||
|
||||
def fix_end_of_track(messages: Iterable[BaseMessage], skip_checks: bool = False) -> Generator[BaseMessage, Never]:
|
||||
"""Remove all end_of_track messages and add one at the end.
|
||||
|
||||
This is used by `merge_tracks()` and `MidiFile.save()`."""
|
||||
|
||||
def merge_tracks(tracks: Iterable[MidiTrack], skip_checks: bool = False) -> MidiTrack:
|
||||
"""Returns a MidiTrack object with all messages from all tracks.
|
||||
|
||||
The messages are returned in playback order with delta times
|
||||
as if they were all in one track.
|
||||
|
||||
Pass `skip_checks=True` to skip validation of messages before merging.
|
||||
This should ONLY be used when the messages in tracks have already
|
||||
been validated by mido.checks.
|
||||
"""
|
||||
@@ -0,0 +1,38 @@
|
||||
from numbers import Real
|
||||
|
||||
type _RealNumber = int | float | Real
|
||||
type _TimeSignature = tuple[int, int]
|
||||
|
||||
def tick2second(tick: _RealNumber, ticks_per_beat: _RealNumber, tempo: _RealNumber) -> float:
|
||||
"""Convert absolute time in ticks to seconds.
|
||||
|
||||
Returns absolute time in seconds for a chosen MIDI file time resolution
|
||||
(ticks/pulses per quarter note, also called PPQN) and tempo (microseconds
|
||||
per quarter note).
|
||||
"""
|
||||
|
||||
def second2tick(second: _RealNumber, ticks_per_beat: _RealNumber, tempo: _RealNumber) -> int:
|
||||
"""Convert absolute time in seconds to ticks.
|
||||
|
||||
Returns absolute time in ticks for a chosen MIDI file time resolution
|
||||
(ticks/pulses per quarter note, also called PPQN) and tempo (microseconds
|
||||
per quarter note). Normal rounding applies.
|
||||
"""
|
||||
|
||||
def bpm2tempo(bpm: _RealNumber, time_signature: _TimeSignature = (4, 4)) -> int:
|
||||
"""Convert BPM (beats per minute) to MIDI file tempo (microseconds per
|
||||
quarter note).
|
||||
|
||||
Depending on the chosen time signature a bar contains a different number of
|
||||
beats. These beats are multiples/fractions of a quarter note, thus the
|
||||
returned BPM depend on the time signature. Normal rounding applies.
|
||||
"""
|
||||
|
||||
def tempo2bpm(tempo: _RealNumber, time_signature: _TimeSignature = (4, 4)) -> float:
|
||||
"""Convert MIDI file tempo (microseconds per quarter note) to BPM (beats
|
||||
per minute).
|
||||
|
||||
Depending on the chosen time signature a bar contains a different number of
|
||||
beats. The beats are multiples/fractions of a quarter note, thus the
|
||||
returned tempo depends on the time signature denominator.
|
||||
"""
|
||||
Reference in New Issue
Block a user