82 lines
2.3 KiB
Python
82 lines
2.3 KiB
Python
|
|
"""
|
||
|
|
MIDI Parser
|
||
|
|
|
||
|
|
There is no need to use this module directly. All you need is
|
||
|
|
available in the top level module.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from collections import deque
|
||
|
|
from collections.abc import Generator, Iterable
|
||
|
|
from numbers import Integral
|
||
|
|
from typing import Never
|
||
|
|
|
||
|
|
from .messages import Message
|
||
|
|
|
||
|
|
class Parser:
|
||
|
|
"""
|
||
|
|
MIDI byte stream parser
|
||
|
|
|
||
|
|
Parses a stream of MIDI bytes and produces messages.
|
||
|
|
|
||
|
|
Data can be put into the parser in the form of
|
||
|
|
integers, byte arrays or byte strings.
|
||
|
|
"""
|
||
|
|
|
||
|
|
messages: deque[Message]
|
||
|
|
|
||
|
|
def __init__(self, data: Iterable[int] | None = ...) -> None: ...
|
||
|
|
def feed(self, data: Iterable[int]) -> None:
|
||
|
|
"""Feed MIDI data to the parser.
|
||
|
|
|
||
|
|
Accepts any object that produces a sequence of integers in
|
||
|
|
range 0..255, such as:
|
||
|
|
|
||
|
|
```
|
||
|
|
[0, 1, 2]
|
||
|
|
(0, 1, 2)
|
||
|
|
[for i in range(256)]
|
||
|
|
(for i in range(256)]
|
||
|
|
bytearray()
|
||
|
|
```
|
||
|
|
"""
|
||
|
|
|
||
|
|
def feed_byte(self, byte: int | Integral) -> None:
|
||
|
|
"""Feed one MIDI byte into the parser.
|
||
|
|
|
||
|
|
The byte must be an integer in range 0..255.
|
||
|
|
"""
|
||
|
|
|
||
|
|
def get_message(self) -> Message | None:
|
||
|
|
"""Get the first parsed message.
|
||
|
|
|
||
|
|
Returns None if there is no message yet. If you don't want to
|
||
|
|
deal with None, you can use `pending()` to see how many messages
|
||
|
|
you can get before you get None, or just iterate over the
|
||
|
|
parser.
|
||
|
|
"""
|
||
|
|
|
||
|
|
def pending(self) -> int:
|
||
|
|
"""Return the number of pending messages."""
|
||
|
|
|
||
|
|
def __len__(self) -> int:
|
||
|
|
"""Return the number of pending messages."""
|
||
|
|
|
||
|
|
def __iter__(self) -> Generator[Message, Never]:
|
||
|
|
"""Yield messages that have been parsed so far."""
|
||
|
|
|
||
|
|
def parse_all(data: Iterable[int] | None) -> list[Message]:
|
||
|
|
"""Parse MIDI data and return a list of all messages found.
|
||
|
|
|
||
|
|
This is typically used to parse a little bit of data with a few
|
||
|
|
messages in it. It's best to use a Parser object for larger
|
||
|
|
amounts of data. Also, tt's often easier to use `parse()` if you
|
||
|
|
know there is only one message in the data.
|
||
|
|
"""
|
||
|
|
|
||
|
|
def parse(data: Iterable[int] | None) -> Message | None:
|
||
|
|
"""Parse MIDI data and return the first message found.
|
||
|
|
|
||
|
|
Data after the first message is ignored. Use `parse_all()`
|
||
|
|
to parse more than one message.
|
||
|
|
"""
|