-
Notifications
You must be signed in to change notification settings - Fork 0
/
convert.py
140 lines (113 loc) · 3.44 KB
/
convert.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
139
140
from typing import Tuple
__NOTENAMES = {
0: 'C',
1: 'C#',
2: 'D',
3: 'Eb',
4: 'E',
5: 'F',
6: 'F#',
7: 'G',
8: 'Ab',
9: 'A',
10: 'Bb',
11: 'B'
}
__NOTENUMSLOWERCASE = {
'b#': 0,
'c': 0,
'c#': 1,
'db': 1,
'd': 2,
'd#': 3,
'eb': 3,
'e': 4,
'fb': 4,
'e#': 5,
'f': 5,
'f#': 6,
'gb': 6,
'g': 7,
'g#': 8,
'ab': 8,
'a': 9,
'a#': 10,
'bb': 10,
'b': 11,
'cb': 11
}
def midinum_to_12edo_name(notenum) -> str:
notenum = int(notenum)
if not (0 <= notenum <= 127):
raise ValueError('note number out of range (0-127)')
octave = -1 + notenum // 12
note = notenum % 12
return __NOTENAMES[note] + str(octave)
def notename_to_midinum(notename: str) -> int:
try:
note, octave = notename[:-1], int(notename[-1])
if note.endswith('-'):
# handle negative octaves
note = note[:-1]
octave = -octave
return __NOTENUMSLOWERCASE[note.lower()] + 12 * (octave + 1)
except Exception:
raise ValueError(f'{notename} is an invalid note name')
def cents_to_pitchbend(cents, pb_range) -> int:
"""
Convert cent offset to pitch bend value (capped to range 0-16383)
:param cents: cent offset
:param pb_range: pitch bend range in either direction (+/-) as per setting on Roli Dashboard / Equator
:return:
"""
pb = 8192 + 16384 * cents / (pb_range * 200)
if 0 > pb > 16383:
print(f'warning: pitchbend range too small to bend {cents} cents')
return int(max(0, min(16383, pb)))
def raw_pitch_msg_to_pitch_bend(ls7, ms7):
"""
Convert the raw midi pitch bend message to a single pitch bend value (0-16383)
:param ls7: Least significant 7 bits (2nd msg byte)
:param ms7: Most significant 7 bits (3rd msg byte)
:return: A pitch bend value (0-16383)
"""
return 2**7 * ms7 + ls7
def pitch_bend_to_raw_pitch_msg(pb) -> Tuple[int, int]:
"""
:param pb: pitch bend amount (will be bounded to 0-16383 if it exceeds range)
:return: (lsb, msb)
"""
pb = max(0, min(16383, pb))
return pb % 2 ** 7, pb // 2 ** 7
def to_relative_slide_output(abs74, init74) -> int:
"""
Converts absolute cc74 slide to relative cc74 slide based
on initial cc74 position.
Assumes a graph of 2 linear gradients. Perhaps a better algorithm
would be to smooth out the curve?
:param abs74: The absolute cc74 value
:param init74: The initial cc74 value when the note was struck
:return: The relative cc74 value to send
"""
if abs74 == init74:
return 64
elif abs74 < init74:
return min(127, max(0, int(64 * abs74 / init74)))
elif abs74 > init74:
return min(127, max(0, int(64 + 64 * (abs74 - init74) / (127 - init74))))
def to_bipolar_slide_output(abs74, init74) -> int:
"""
Converts absolute cc74 slide to bipolar cc74 slide based
on initial cc74 position.
Assumes a graph of 2 linear gradients. Perhaps a better algorithm
would be to smooth out the curve?
:param abs74: The absolute cc74 value
:param init74: The initial cc74 value when the note was struck
:return: The relative cc74 value to send
"""
if abs74 == init74:
return 0
elif abs74 < init74:
return min(127, max(0, int(127 * (init74 - abs74) / init74)))
elif abs74 > init74:
return min(127, max(0, int(127 * (abs74 - init74) / (127 - init74))))