""" 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]: ...