-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathdevice_eventforward.py
138 lines (109 loc) · 4 KB
/
device_eventforward.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# name=Universal Event Forwarder
# url=https://github.com/MiguelGuthridge/Universal-Controller-Script
"""
device_eventforward
This script packages events into a format where they can be distinguished
and forwards the event on to the main controller. It can be used to link
multiple ports of a device together in a way such that overlapping event
IDs can be distinguished. The additional forwarding takes less than 1 ms, so
performance impact is negligible.
Authors:
* Miguel Guthridge
# Specification:
Each event is wrapped by a sysex event that contains the details for the event
as required
* 0xF0 sysex start
* 0x7D non-commercial system exclusive ID (should prevent overlap with any other
hardware which would be using a registered system exclusive ID)
* [device name] a null-terminated string containing the device name (sans the
MIDIIN# parts), to allow for the events to be filtered if the universal
controller is being used for multiple devices
* 0x00 null terminator
* [device number] determined by the device name in FL Studio, which has
something like MIDIIN# where # is the device number. This allows the script
to determine what sub-device the message came from, and separate the events
properly.
* [event category] type of event:
* standard (0), or
* sysex (1)
* [event data] data from the event:
* data2, data1, status if standard event
* sysex data if sysex event
* 0xF7 event terminator (if standard event)
# Limitations:
* This system may not work correctly on MacOS, as device names could be managed
differently to Windows (they are determined by OS-specific APIs)
* Current algorithm assumes less than 10 extra MIDIIN# devices
* Doesn't support devices with parentheses in device name
"""
import include
import time
from typing import TYPE_CHECKING
from common import log, verbosity
from common.consts import getVersionString, ASCII_HEADER_ART
from common.util.events import bytesToString, eventToString
from common.util.misc import formatLongTime
from common.types import eventData
import device
EVENT_HEADER: bytes = bytes()
def raiseIncompatibleDevice():
raise TypeError("This script should be used to forward extra device "
"port events to the primary device port. This isn't a "
"device port.")
def OnMidiIn(event: eventData):
if event.sysex is None:
if TYPE_CHECKING:
assert event.data2 is not None
assert event.data1 is not None
assert event.status is not None
output = EVENT_HEADER + bytes([0]) + bytes([
event.data2,
event.data1,
event.status,
0xF7
])
else:
output = EVENT_HEADER + bytes([1]) + bytes(event.sysex)
# print("Output sysex event:")
# print(bytesToString(output))
# Dispatch to all available devices
for i in range(device.dispatchReceiverCount()):
device.dispatch(i, 0xF0, output)
log(
"device.forward.out",
"Dispatched event to main script: " + eventToString(event)
)
event.handled = True
def OnInit():
global EVENT_HEADER
# Determine device name and number
name = device.getName()
if not name.startswith("MIDIIN"):
raiseIncompatibleDevice()
name = name.lstrip("MIDIIN")
bracket_start = name.find("(")
if bracket_start == 0:
raiseIncompatibleDevice()
try:
dev_num = int(name[0:bracket_start])
except ValueError:
raiseIncompatibleDevice()
name = name[bracket_start+1:-1]
EVENT_HEADER = bytes([
0xF0, # Start sysex
0x7D # Non-commercial sysex ID
]) + name.encode() \
+ bytes([0]) \
+ bytes([dev_num])
log(
"device.forward.bootstrap",
"Generated event header",
detailed_msg=f"{bytesToString(EVENT_HEADER)}\n"
f"{dev_num=}"
)
log(
"device.forward.bootstrap",
"Loaded script successfully"
)
print(ASCII_HEADER_ART)
print(f"Universal Event Forwarder: v{getVersionString()}")