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,234 @@
|
||||
"""
|
||||
Useful tools for working with ports
|
||||
"""
|
||||
|
||||
import threading
|
||||
from collections import deque
|
||||
from collections.abc import Collection, Generator, Iterable
|
||||
from typing import Any, Literal, Never, Self, overload
|
||||
|
||||
from .messages import Message
|
||||
from .parser import Parser
|
||||
|
||||
# How many seconds to sleep before polling again.
|
||||
_DEFAULT_SLEEP_TIME: float
|
||||
_sleep_time: int | float
|
||||
|
||||
def sleep() -> None:
|
||||
"""Sleep for N seconds.
|
||||
|
||||
This is used in ports when polling and waiting for messages. N can
|
||||
be set with `set_sleep_time()`."""
|
||||
|
||||
def set_sleep_time(seconds: int | float = ...) -> None:
|
||||
"""Set the number of seconds `sleep()` will sleep."""
|
||||
|
||||
def get_sleep_time() -> int | float:
|
||||
"""Get number of seconds `sleep()` will sleep."""
|
||||
|
||||
def reset_messages() -> Generator[Message, Never]:
|
||||
"""Yield "All Notes Off" and "Reset All Controllers" for all channels"""
|
||||
|
||||
def panic_messages() -> Generator[Message, Never]:
|
||||
"""Yield "All Sounds Off" for all channels.
|
||||
|
||||
This will mute all sounding notes regardless of
|
||||
envelopes. Useful when notes are hanging and nothing else
|
||||
helps.
|
||||
"""
|
||||
|
||||
class DummyLock:
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(self, *_: object) -> Literal[False]: ...
|
||||
|
||||
class BasePort:
|
||||
"""
|
||||
Abstract base class for Input and Output ports.
|
||||
"""
|
||||
|
||||
is_input: bool = False
|
||||
is_output: bool = False
|
||||
_locking: bool = True
|
||||
|
||||
name: bool
|
||||
closed: bool
|
||||
_lock: DummyLock | threading.RLock
|
||||
|
||||
def __init__(self, name: str | None = None, **kwargs: object) -> None: ...
|
||||
def _open(self, **kwargs: object) -> None: ...
|
||||
def _close(self) -> None: ...
|
||||
def close(self) -> None:
|
||||
"""Close the port.
|
||||
|
||||
If the port is already closed, nothing will happen. The port
|
||||
is automatically closed when the object goes out of scope or
|
||||
is garbage collected.
|
||||
"""
|
||||
|
||||
def __del__(self) -> None: ...
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(self, *_) -> Literal[False]: ...
|
||||
|
||||
class BaseInput(BasePort):
|
||||
"""Base class for input port.
|
||||
|
||||
Subclass and override `_receive()` to create a new input port type.
|
||||
(See `portmidi.py` for an example of how to do this.)
|
||||
"""
|
||||
|
||||
is_input: bool = True
|
||||
|
||||
_parser: Parser
|
||||
_messages: deque[Message]
|
||||
|
||||
def __init__(self, name: str = "", **kwargs: Any) -> None:
|
||||
"""Create an input port.
|
||||
|
||||
name is the port name, as returned by `input_names()`. If
|
||||
name is not passed, the default input is used instead.
|
||||
"""
|
||||
|
||||
def _receive(self, block: bool = True) -> Message: ...
|
||||
def iter_pending(self) -> Generator[Message, Never]:
|
||||
"""Iterate through pending messages."""
|
||||
|
||||
@overload
|
||||
def receive(self, block: Literal[True] = True) -> Message:
|
||||
"""Return the next message.
|
||||
|
||||
This will block until a message arrives.
|
||||
|
||||
If you pass `block=False` it will not block and instead return
|
||||
None if there is no available message.
|
||||
|
||||
If the port is closed and there are no pending messages IOError
|
||||
will be raised. If the port closes while waiting inside `receive()`,
|
||||
IOError will be raised. TODO: this seems a bit inconsistent. Should
|
||||
different errors be raised? What's most useful here?
|
||||
"""
|
||||
@overload
|
||||
def receive(self, block: bool) -> Message | None: ...
|
||||
def poll(self) -> Message | None:
|
||||
"""Receive the next pending message or None
|
||||
|
||||
This is the same as calling `receive(block=False)`."""
|
||||
|
||||
def __iter__(self) -> Generator[Message, Never]:
|
||||
"""Iterate through messages until the port closes."""
|
||||
|
||||
class BaseOutput(BasePort):
|
||||
"""
|
||||
Base class for output port.
|
||||
|
||||
Subclass and override `_send()` to create a new port type. (See
|
||||
`portmidi.py` for how to do this.)
|
||||
"""
|
||||
|
||||
is_output: bool = True
|
||||
|
||||
autoreset: bool
|
||||
|
||||
def __init__(self, name: str = "", autoreset: bool = False, **kwargs: object) -> None:
|
||||
"""Create an output port
|
||||
|
||||
name is the port name, as returned by `output_names()`. If
|
||||
name is not passed, the default output is used instead.
|
||||
"""
|
||||
|
||||
def _send(self, msg: Message) -> None: ...
|
||||
def send(self, msg: Message) -> None:
|
||||
"""Send a message on the port.
|
||||
|
||||
A copy of the message will be sent, so you can safely modify
|
||||
the original message without any unexpected consequences.
|
||||
"""
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Send "All Notes Off" and "Reset All Controllers" on all channels"""
|
||||
|
||||
def panic(self) -> None:
|
||||
"""Send "All Sounds Off" on all channels.
|
||||
|
||||
This will mute all sounding notes regardless of
|
||||
envelopes. Useful when notes are hanging and nothing else
|
||||
helps.
|
||||
"""
|
||||
|
||||
class BaseIOPort(BaseInput, BaseOutput): # pyright: ignore[reportUnsafeMultipleInheritance]
|
||||
def __init__(self, name: str = "", **kwargs: object) -> None:
|
||||
"""Create an IO port.
|
||||
|
||||
name is the port name, as returned by ioport_names().
|
||||
"""
|
||||
|
||||
# NOTE: Generics not supported in runtime
|
||||
class IOPort[InT: BaseInput = BaseInput, OutT: BaseOutput = BaseOutput](BaseIOPort):
|
||||
"""Input / output port.
|
||||
|
||||
This is a convenient wrapper around an input port and an output
|
||||
port which provides the functionality of both. Every method call
|
||||
is forwarded to the appropriate port.
|
||||
"""
|
||||
|
||||
_locking: bool = False
|
||||
|
||||
input: InT
|
||||
output: OutT
|
||||
|
||||
def __init__(self, input: InT, output: OutT) -> None: ...
|
||||
|
||||
class EchoPort(BaseIOPort): ...
|
||||
|
||||
class MultiPort(BaseIOPort):
|
||||
def __init__(self, ports: Iterable[BaseIOPort], yield_ports: bool = False) -> None: ...
|
||||
|
||||
@overload
|
||||
def multi_receive[T: BaseInput](
|
||||
ports: Collection[T], yield_ports: Literal[True], block: bool = True
|
||||
) -> Generator[tuple[T, Message], Never]: ...
|
||||
@overload
|
||||
def multi_receive(
|
||||
ports: Collection[BaseInput], yield_ports: Literal[False] = False, block: bool = True
|
||||
) -> Generator[Message, Never]: ...
|
||||
@overload
|
||||
def multi_receive[T: BaseInput](
|
||||
ports: Collection[T], yield_ports: bool, block: bool = True
|
||||
) -> Generator[tuple[T, Message] | Message, Never]: ...
|
||||
def multi_receive[T: BaseInput](
|
||||
ports: Collection[T], yield_ports: bool, block: bool = True
|
||||
) -> Generator[tuple[T, Message] | Message, Never]:
|
||||
"""Receive messages from multiple ports.
|
||||
|
||||
Generates messages from ever input port. The ports are polled in
|
||||
random order for fairness, and all messages from each port are
|
||||
yielded before moving on to the next port.
|
||||
|
||||
If `yield_ports=True`, `(port, message)` is yielded instead of just
|
||||
the message.
|
||||
|
||||
If `block=False` only pending messages will be yielded.
|
||||
"""
|
||||
|
||||
@overload
|
||||
def multi_iter_pending[T: BaseInput](
|
||||
ports: Collection[T], yield_ports: Literal[True]
|
||||
) -> Generator[tuple[T, Message], Never]: ...
|
||||
@overload
|
||||
def multi_iter_pending(
|
||||
ports: Collection[BaseInput], yield_ports: Literal[False] = False
|
||||
) -> Generator[Message, Never]: ...
|
||||
@overload
|
||||
def multi_iter_pending[T: BaseInput](
|
||||
ports: Collection[T], yield_ports: bool
|
||||
) -> Generator[tuple[T, Message] | Message, Never]: ...
|
||||
def multi_iter_pending[T: BaseInput](
|
||||
ports: Collection[T], yield_ports: bool
|
||||
) -> Generator[tuple[T, Message] | Message, Never]:
|
||||
"""Iterate through all pending messages in ports.
|
||||
|
||||
This is the same as calling `multi_receive(ports, block=False)`.
|
||||
The function is kept around for backwards compatability.
|
||||
"""
|
||||
|
||||
def multi_send(ports: Iterable[BaseOutput], msg: Message) -> None:
|
||||
"""Send message on all ports."""
|
||||
Reference in New Issue
Block a user