-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.py
117 lines (100 loc) · 3.55 KB
/
utils.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
def lhex(n: int, l: int = 8):
return "0x" + hex(n)[2:].zfill(l) #'0x002a'
def bhex(b: bytes, l: int = 8):
n = int.from_bytes(b, 'big')
return lhex(n, l)
def nlhex(n: int, l: int = 8, g: int = 4):
s = hex(n)[2:].zfill(l)
o = ""
i = 0
while i < len(s):
o += f"{s[i:i+g]} "
i += g
# o += f"{s[i-g:i]}."
return o
class midi7bitrange:
def __init__(self, start: int, stop: int = None, step: int = None):
if stop is None:
stop = start
start = 0
if step is None:
step = 1
assert int(step) == step and start == int(start) and stop == int(stop)
assert step != 0
self.step = step
self.start = start
self.stop = stop
self.positive = step > 0
def all_numbers(self):
n = self.start
0b0010100_0000000_0000000
class floatrange:
def __init__(self, start: float, stop: float = None, step: float = None):
if stop is None:
stop = start
start = 0.0
if step is None:
step = 1.0
assert step > 0
self.step = step
self.start = start
self.stop = stop
def __len__(self):
return int((self.stop - self.start) / self.step)
def __iter__(self):
n = self.start
while n < self.stop:
yield n
n += self.step
def __repr__(self):
return f"floatrange(start={self.start}, stop={self.stop}, step={self.step})"
def roland_checksum(address_bytes: tuple, data_bytes: tuple, base_checksum_val: int = 0) -> int:
chk = (128 - ((sum(a for a in address_bytes) + sum(a for a in data_bytes) + base_checksum_val) % 128))
if chk == 128:
chk = 0
return chk
def rollover_arithmetic(address: int, offset: int, max_length: int = 4, bits: int = 7):
# help do unholy arithmetic of n-bit with n < 8
# like MIDI expects us to do with 7 bit
address_as_bytes = address.to_bytes(max_length, 'little')
offset_as_bytes = offset.to_bytes(max_length, 'little')
out = []
carry = 0
for n, m in zip(address_as_bytes, offset_as_bytes):
s = n + m + carry
carry = 0
if s > (1 << bits) - 1:
s = s - (1 << bits)
carry = 1
out.append(s)
return int.from_bytes(out, 'little')
def roland_address_arithmetic(address: int, stride: int):
# take an address in hex form
# if address + stride has any bit over 7f
# subtract 0x80 from it
# carry the more significant bit to + 1
return rollover_arithmetic(address, stride, 4, 7)
class roland_address_strider:
"""Given a first offset, last offset, and stride, provide the succession of addresses"""
def __init__(self, first_offset: int, end_offset: int, stride: int):
self.first_offset = first_offset
self.end_offset = end_offset
if stride == 0:
raise ValueError("roland_address_strider: stride can never be 0")
self.stride = stride
self._aslist_ = None
def _aslist(self):
self._aslist_ = list(iter(self))
def __iter__(self):
a = self.first_offset
while a <= self.end_offset:
yield a
a = roland_address_arithmetic(a, self.stride)
def __len__(self):
return len(list(iter(self)))
def __getitem__(self, key):
if self._aslist_ is None:
self._aslist()
return self._aslist_[key]
def __repr__(self):
return f'roland_address_strider(first_offset={self.first_offset}, end_offset={self.end_offset}, stride={self.stride})'