9009a7c5bc
- 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.
155 lines
5.5 KiB
Python
155 lines
5.5 KiB
Python
"""
|
|
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]: ...
|