pyMIDIspy 1.0.0__cp314-cp314-macosx_10_13_universal2.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymidispy-1.0.0.data/purelib/pyMIDIspy/__init__.py +168 -0
- pymidispy-1.0.0.data/purelib/pyMIDIspy/core.py +1205 -0
- pymidispy-1.0.0.data/purelib/pyMIDIspy/midi_utils.py +430 -0
- pymidispy-1.0.0.dist-info/METADATA +436 -0
- pymidispy-1.0.0.dist-info/RECORD +8 -0
- pymidispy-1.0.0.dist-info/WHEEL +5 -0
- pymidispy-1.0.0.dist-info/licenses/LICENSE +27 -0
- pymidispy-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pyMIDIspy - Python MIDI capture for macOS
|
|
3
|
+
|
|
4
|
+
A Python library for capturing MIDI messages on macOS using the
|
|
5
|
+
SnoizeMIDISpy framework. This enables monitoring of MIDI data that is sent
|
|
6
|
+
to any MIDI destination, not just receiving incoming MIDI.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from pyMIDIspy import MIDIOutputClient, get_destinations
|
|
10
|
+
|
|
11
|
+
# List available MIDI destinations
|
|
12
|
+
for dest in get_destinations():
|
|
13
|
+
print(f"{dest.name} (ID: {dest.unique_id})")
|
|
14
|
+
|
|
15
|
+
# Create an output client and monitor a destination
|
|
16
|
+
def on_midi_message(messages, source_endpoint):
|
|
17
|
+
for msg in messages:
|
|
18
|
+
print(f"Captured: {msg}")
|
|
19
|
+
|
|
20
|
+
client = MIDIOutputClient(callback=on_midi_message)
|
|
21
|
+
client.connect_destination(destination_unique_id)
|
|
22
|
+
|
|
23
|
+
# Keep running...
|
|
24
|
+
try:
|
|
25
|
+
import time
|
|
26
|
+
while True:
|
|
27
|
+
time.sleep(0.1)
|
|
28
|
+
finally:
|
|
29
|
+
client.close()
|
|
30
|
+
|
|
31
|
+
Requirements:
|
|
32
|
+
- macOS only
|
|
33
|
+
- The SnoizeMIDISpy.framework must be installed and accessible
|
|
34
|
+
- The MIDI spy driver must be installed (call install_driver_if_necessary())
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from .core import (
|
|
38
|
+
# Clients
|
|
39
|
+
MIDIOutputClient,
|
|
40
|
+
MIDIInputClient,
|
|
41
|
+
# Exceptions
|
|
42
|
+
MIDISpyError,
|
|
43
|
+
DriverMissingError,
|
|
44
|
+
DriverCommunicationError,
|
|
45
|
+
ConnectionExistsError,
|
|
46
|
+
ConnectionNotFoundError,
|
|
47
|
+
# Functions
|
|
48
|
+
install_driver_if_necessary,
|
|
49
|
+
get_destinations,
|
|
50
|
+
get_destination_by_unique_id,
|
|
51
|
+
get_sources,
|
|
52
|
+
get_source_by_unique_id,
|
|
53
|
+
# Data classes
|
|
54
|
+
MIDIDestination,
|
|
55
|
+
MIDISource,
|
|
56
|
+
MIDIMessage,
|
|
57
|
+
# Framework utilities
|
|
58
|
+
_find_framework,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
from .midi_utils import (
|
|
62
|
+
parse_midi_message,
|
|
63
|
+
ParsedMIDIMessage,
|
|
64
|
+
MessageFilter,
|
|
65
|
+
note_name,
|
|
66
|
+
note_number,
|
|
67
|
+
controller_name,
|
|
68
|
+
# MIDI status constants
|
|
69
|
+
NOTE_OFF,
|
|
70
|
+
NOTE_ON,
|
|
71
|
+
POLY_PRESSURE,
|
|
72
|
+
CONTROL_CHANGE,
|
|
73
|
+
PROGRAM_CHANGE,
|
|
74
|
+
CHANNEL_PRESSURE,
|
|
75
|
+
PITCH_BEND,
|
|
76
|
+
SYSEX_START,
|
|
77
|
+
TIMING_CLOCK,
|
|
78
|
+
START,
|
|
79
|
+
CONTINUE,
|
|
80
|
+
STOP,
|
|
81
|
+
# Message type constants for filtering
|
|
82
|
+
MSG_NOTE_OFF,
|
|
83
|
+
MSG_NOTE_ON,
|
|
84
|
+
MSG_NOTE,
|
|
85
|
+
MSG_POLY_PRESSURE,
|
|
86
|
+
MSG_CONTROL_CHANGE,
|
|
87
|
+
MSG_PROGRAM_CHANGE,
|
|
88
|
+
MSG_CHANNEL_PRESSURE,
|
|
89
|
+
MSG_PITCH_BEND,
|
|
90
|
+
MSG_SYSEX,
|
|
91
|
+
MSG_TIMING_CLOCK,
|
|
92
|
+
MSG_TRANSPORT,
|
|
93
|
+
MSG_ACTIVE_SENSING,
|
|
94
|
+
MSG_REALTIME,
|
|
95
|
+
MSG_CHANNEL,
|
|
96
|
+
MSG_SYSTEM,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
# Core classes
|
|
101
|
+
"MIDIOutputClient", # Capture outgoing MIDI (requires spy driver)
|
|
102
|
+
"MIDIInputClient", # Receive incoming MIDI (standard CoreMIDI)
|
|
103
|
+
"MIDIDestination",
|
|
104
|
+
"MIDISource",
|
|
105
|
+
"MIDIMessage",
|
|
106
|
+
# Filtering
|
|
107
|
+
"MessageFilter",
|
|
108
|
+
# Exceptions
|
|
109
|
+
"MIDISpyError",
|
|
110
|
+
"DriverMissingError",
|
|
111
|
+
"DriverCommunicationError",
|
|
112
|
+
"ConnectionExistsError",
|
|
113
|
+
"ConnectionNotFoundError",
|
|
114
|
+
# Functions
|
|
115
|
+
"install_driver_if_necessary",
|
|
116
|
+
"get_destinations",
|
|
117
|
+
"get_destination_by_unique_id",
|
|
118
|
+
"get_sources",
|
|
119
|
+
"get_source_by_unique_id",
|
|
120
|
+
"get_framework_path",
|
|
121
|
+
# MIDI utilities
|
|
122
|
+
"parse_midi_message",
|
|
123
|
+
"ParsedMIDIMessage",
|
|
124
|
+
"note_name",
|
|
125
|
+
"note_number",
|
|
126
|
+
"controller_name",
|
|
127
|
+
# MIDI status byte constants
|
|
128
|
+
"NOTE_OFF",
|
|
129
|
+
"NOTE_ON",
|
|
130
|
+
"POLY_PRESSURE",
|
|
131
|
+
"CONTROL_CHANGE",
|
|
132
|
+
"PROGRAM_CHANGE",
|
|
133
|
+
"CHANNEL_PRESSURE",
|
|
134
|
+
"PITCH_BEND",
|
|
135
|
+
"SYSEX_START",
|
|
136
|
+
"TIMING_CLOCK",
|
|
137
|
+
"START",
|
|
138
|
+
"CONTINUE",
|
|
139
|
+
"STOP",
|
|
140
|
+
# Message type constants for filtering
|
|
141
|
+
"MSG_NOTE_OFF",
|
|
142
|
+
"MSG_NOTE_ON",
|
|
143
|
+
"MSG_NOTE",
|
|
144
|
+
"MSG_POLY_PRESSURE",
|
|
145
|
+
"MSG_CONTROL_CHANGE",
|
|
146
|
+
"MSG_PROGRAM_CHANGE",
|
|
147
|
+
"MSG_CHANNEL_PRESSURE",
|
|
148
|
+
"MSG_PITCH_BEND",
|
|
149
|
+
"MSG_SYSEX",
|
|
150
|
+
"MSG_TIMING_CLOCK",
|
|
151
|
+
"MSG_TRANSPORT",
|
|
152
|
+
"MSG_ACTIVE_SENSING",
|
|
153
|
+
"MSG_REALTIME",
|
|
154
|
+
"MSG_CHANNEL",
|
|
155
|
+
"MSG_SYSTEM",
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
__version__ = "1.0.0"
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get_framework_path():
|
|
162
|
+
"""
|
|
163
|
+
Get the path to the bundled SnoizeMIDISpy framework.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
str: Path to the framework dylib, or None if not found.
|
|
167
|
+
"""
|
|
168
|
+
return _find_framework()
|