Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pedalboard/midi_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def normalize_midi_messages(_input) -> List[Tuple[bytes, float]]:
into a juce::MidiBuffer on the C++ side.
"""
output = []
for message in _input:
for index, message in enumerate(_input):
if hasattr(message, "bytes") and hasattr(message, "time"):
output.append((bytes(message.bytes()), message.time))
elif (isinstance(message, tuple) or isinstance(message, list)) and len(message) == 2:
Expand All @@ -57,6 +57,13 @@ def normalize_midi_messages(_input) -> List[Tuple[bytes, float]]:
elif not isinstance(message, bytes):
message = bytes(message)
output.append((message, time))
else:
raise TypeError(
f"Unable to interpret MIDI message at index {index}: {message!r}. "
"MIDI messages must either be objects with `bytes` and `time` attributes "
"(like mido.Message) or (message, timestamp) tuples, where timestamp is "
"the number of seconds from the start of the returned audio buffer."
)

# Detect the case in which the provided timestamps
# are likely delta values rather than absolute values:
Expand Down
26 changes: 26 additions & 0 deletions tests/test_midi_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,29 @@
)
def test_mido_normalization(_input, expected: List[Tuple[bytes, float]]):
assert normalize_midi_messages(_input) == expected


@pytest.mark.parametrize(
"malformed",
[
pytest.param((bytes([0x90, 60, 64]),), id="one_tuple_missing_timestamp"),
pytest.param((bytes([0x90, 60, 64]), 0.0, 99), id="three_tuple_extra_element"),
pytest.param(bytes([0x90, 60, 64]), id="bare_bytes_without_timestamp"),
pytest.param(None, id="none"),
pytest.param(42, id="bare_int"),
],
)
def test_malformed_messages_raise(malformed):
"""Malformed messages must raise rather than being silently dropped."""
messages = [
(bytes([0x90, 60, 64]), 0.0),
malformed,
(bytes([0x80, 60, 64]), 1.0),
]
with pytest.raises(TypeError, match="index 1"):
normalize_midi_messages(messages)


def test_valid_messages_are_unaffected():
messages = [(bytes([0x90, 60, 64]), 0.0), (bytes([0x80, 60, 64]), 1.0)]
assert normalize_midi_messages(messages) == messages