Skip to content

Commit 71def6b

Browse files
committed
Fixed suspend issue, Added swap/rotate lp options, Added configurable RPN delay, Added compose/decompose pitch bend for upcoming microtonal modes
1 parent 4017b18 commit 71def6b

File tree

8 files changed

+148
-39
lines changed

8 files changed

+148
-39
lines changed

midimech.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
# print("The project dependencies have changed! Run the requirements setup command again!")
2727
# sys.exit(1)
2828

29+
def error(err):
30+
print(err)
31+
input()
32+
sys.exit(1)
33+
2934
try:
3035
import launchpad_py as launchpad
3136
except ImportError:
@@ -46,7 +51,6 @@
4651
except ImportError:
4752
error("The project dependencies have changed! Run the requirements setup command again!")
4853

49-
5054
def main():
5155
core = None
5256
try:

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ launchpad-py
66
musicpy
77
pyyaml
88
webcolors
9+
easygui

scripts/build_windows_package.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
cd ..
2-
python -m PyInstaller --onefile midimech.py
2+
python -m PyInstaller --icon=icon.ico --onefile midimech.py
33
cd scripts

src/articulation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
from enum import Enum
1+
22
from src.util import *
33
from src.constants import *
4+
from enum import Enum
45

56
class Articulation:
67
State = Enum('state', 'off pre attack hold release')

src/core.py

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@
1717
from src.articulation import Articulation
1818
# from src.gamepad import Gamepad
1919

20+
GRADIENT = False
21+
22+
try:
23+
from easygui import msgbox
24+
except ImportError:
25+
print("The project dependencies have changed! Run the requirements setup command again!")
26+
sys.exit(1)
27+
28+
msgbox("test")
29+
2030
with open(os.devnull, "w") as devnull:
2131
# suppress pygame messages (to keep console output clean)
2232
stdout = sys.stdout
@@ -26,25 +36,30 @@
2636
sys.stdout = stdout
2737
import pygame_gui
2838

39+
def error(err):
40+
msgbox(err)
41+
sys.exit(1)
42+
43+
def dependency_error():
44+
error("The project dependencies have changed! Run the requirements setup command again!")
45+
2946
try:
3047
import launchpad_py as launchpad
3148
except ImportError:
3249
try:
3350
import launchpad
3451
except ImportError:
35-
error("The project dependencies have changed! Run the requirements setup command again!")
52+
dependency_error()
3653

3754
try:
3855
import yaml
3956
except ImportError:
40-
error("The project dependencies have changed! Run the requirements setup command again!")
41-
42-
# import mido
57+
dependency_error()
4358

4459
try:
4560
import musicpy as mp
4661
except ImportError:
47-
error("The project dependencies have changed! Run the requirements setup command again!")
62+
dependency_error()
4863

4964
class Core:
5065
CORE = None
@@ -150,17 +165,30 @@ def send_all_notes_off(self):
150165
return
151166
# for ch in range(0,15):
152167
ch = 0
153-
self.midi_write(self.midi_out, [0xb0 | ch, 120, 0], 0)
154-
self.midi_write(self.midi_out, [0xb0 | ch, 123, 0], 0)
168+
self.midi_write(self.midi_out, [0xb0 | ch, 120, 0], 0) # all sounds off
169+
self.midi_write(self.midi_out, [0xb0 | ch, 123, 0], 0) # all notes off
155170

156171
def ls_color(self, x, y, col):
157172
"""Set LinnStrument pad color"""
158173
if self.linn_out:
159174
self.send_ls_cc(0, 20, x + 1)
160175
self.send_ls_cc(0, 21, self.board_h - y - 1)
161176
self.send_ls_cc(0, 22, col)
177+
# time.sleep(self.options.rpn_delay)
178+
179+
def transform_launchpad_coord(self, idx, x, y):
180+
if self.launchpads[idx].rot:
181+
x, y = y, x
182+
x = (8-x-1)
183+
return x, y
162184

163-
def set_light(self, x, y, col, index=None, mark=False): # col is [1,11], 0 resets
185+
def untransform_launchpad_coord(self, idx, x, y):
186+
if self.launchpads[idx].rot:
187+
x = (8-x-1)
188+
x, y = y, x
189+
return x, y
190+
191+
def set_light(self, x, y, col, index=None, mark=False, transform=True): # col is [1,11], 0 resets
164192
"""Set light to color `col` at x, y if in range and connected"""
165193
if y < 0 or y >= self.board_h:
166194
return
@@ -186,19 +214,23 @@ def set_light(self, x, y, col, index=None, mark=False): # col is [1,11], 0 rese
186214
lp_col = 0
187215
else:
188216
lp_col = ivec3(0)
189-
if 0 <= x < 8 and 0 <= y < 8:
190-
if not self.is_macro_button(x, y):
217+
if transform:
218+
xx, yy = self.transform_launchpad_coord(lp.index, x, y)
219+
else:
220+
xx, yy = x, y
221+
if 0 <= xx < 8 and 0 <= yy < 8:
222+
if not self.is_macro_button(xx, yy):
191223
if self.options.launchpad_colors:
192-
lp.out.LedCtrlXYByCode(x, y+1, lp_col)
224+
lp.out.LedCtrlXYByCode(xx, yy+1, lp_col)
193225
else:
194-
lp.out.LedCtrlXY(x, y+1, lp_col[0], lp_col[1], None if lp_col[2] == 0 else lp_col[2])
226+
lp.out.LedCtrlXY(xx, yy+1, lp_col[0], lp_col[1], None if lp_col[2] == 0 else lp_col[2])
195227
else:
196228
if self.options.launchpad_colors:
197-
lp.out.LedCtrlXYByCode(x, y+1, 3)
229+
lp.out.LedCtrlXYByCode(xx, yy+1, 3)
198230
else:
199-
lp.out.LedCtrlXY(x, y+1, 63, 63, 63)
231+
lp.out.LedCtrlXY(xx, yy+1, 63, 63, 63)
200232

201-
def reset_light(self, x, y, reset_red=True):
233+
def reset_light(self, x, y, reset_red=True, transform=True):
202234
"""Reset the light at x, y"""
203235
note = self.get_note_index(x, y, transpose=False)
204236

@@ -223,10 +255,10 @@ def reset_light(self, x, y, reset_red=True):
223255
except IndexError:
224256
light_col = 7
225257

226-
self.set_light(x, y, light_col, note)
258+
self.set_light(x, y, light_col, note, transform=transform)
227259
self.mark_lights[y][x] = False
228260

229-
def reset_launchpad_light(self, x, y, launchpad=None):
261+
def reset_launchpad_light(self, x, y, launchpad=None, transform=True):
230262
"""Reset the launchpad light at x, y"""
231263
note = self.get_note_index(x, 8-y-1, transpose=False)
232264
# if self.is_split():
@@ -238,18 +270,23 @@ def reset_launchpad_light(self, x, y, launchpad=None):
238270
# else:
239271
# light_col = self.options.lights[note]
240272
for lp in ([launchpad] if launchpad else self.launchpads):
241-
self.set_launchpad_light(x, y, note)
273+
self.set_launchpad_light(x, y, note, transform=True)
242274

243-
def set_mark_light(self, x, y, state=True, launchpad=None):
275+
# transform: rotate launchpad if in rotated mode
276+
def set_mark_light(self, x, y, state=True, launchpad=None, transform=True):
244277
"""Set launchpad light to touched color"""
245278
self.mark_lights[y][x] = state
246279
for lp in ([launchpad] if launchpad else self.launchpads):
247280
lp_col = self.options.mark_color
248281
if state:
249-
lp.out.LedCtrlXY(x, y, lp_col[0], lp_col[1], lp_col[2])
282+
if transform:
283+
xx, yy = self.transform_launchpad_coord(lp.index, x, y)
284+
else:
285+
xx, yy = x, y
286+
lp.out.LedCtrlXY(xx, yy, lp_col[0], lp_col[1], lp_col[2])
250287

251288
# `color` below is an scale index (0, 1, 2...)
252-
def set_launchpad_light(self, x, y, color, launchpad=None):
289+
def set_launchpad_light(self, x, y, color, launchpad=None, transform=True):
253290
"""Set launchpad light to color index"""
254291
if self.is_macro_button(x, 8 - y - 1):
255292
if self.options.launchpad_colors:
@@ -275,10 +312,14 @@ def set_launchpad_light(self, x, y, color, launchpad=None):
275312
col = self.options.mark_color / 4
276313

277314
for lp in ([launchpad] if launchpad else self.launchpads):
315+
if transform:
316+
xx, yy = self.transform_launchpad_coord(lp.index, x, y)
317+
else:
318+
xx, yy = x, y
278319
if self.options.launchpad_colors:
279320
lp.out.LedCtrlXYByCode(x, 8-y, col)
280321
else:
281-
lp.out.LedCtrlXY(x, 8-y, col[0], col[1], col[2])
322+
lp.out.LedCtrlXY(xx, 8-yy, col[0], col[1], col[2])
282323

283324
def setup_lights(self):
284325
"""Set all lights"""
@@ -549,7 +590,7 @@ def next_free_note(self):
549590
def is_mpe(self):
550591
return self.options.one_channel == 0
551592

552-
def note_on(self, data, timestamp, width=None, curve=True, mpe=None, octave=0, transpose=0, force_channel=None):
593+
def note_on(self, data, timestamp, width=None, curve=True, mpe=None, octave=0, transpose=0, force_channel=None, bend=0.0):
553594
# if mpe is None:
554595
# mpe = self.options.mpe
555596
d0 = data[0]
@@ -709,6 +750,18 @@ def note_on(self, data, timestamp, width=None, curve=True, mpe=None, octave=0, t
709750
self.note_set.add(midinote)
710751
self.dirty_chord = True
711752

753+
if GRADIENT:
754+
if x > 6:
755+
bend = (x-6) - y/2
756+
bend *= (1/6)
757+
try:
758+
# print(bend)
759+
pitch_lsb, pitch_msb = compose_pitch_bend(bend, 1/12)
760+
# print(pitch_msb, pitch_lsb)
761+
self.midi_write(self.midi_out, [0xE0 | ch, pitch_lsb, pitch_msb], timestamp)
762+
except Exception as e:
763+
print(e)
764+
712765
if self.is_split():
713766
if split_chan == 0:
714767
# self.midi_out.write([[data, ev[1]]]
@@ -1133,6 +1186,7 @@ def cb_launchpad_in(self, lp, event, timestamp=0):
11331186
note = y * 8 + x
11341187
note += 12
11351188
if not self.is_macro_button(x, 8 - y - 1):
1189+
# x, y = self.transform_launchpad_coord(lp.index, x, y)
11361190
self.note_on([160, note, event[2]], timestamp, width=8, transpose=lp.transpose, octave=lp.get_octave(), force_channel=self.options.launchpad_channel)
11371191
self.articulation.pressure(vel / 127)
11381192
else:
@@ -1141,8 +1195,10 @@ def cb_launchpad_in(self, lp, event, timestamp=0):
11411195
x = event[0]
11421196
y = 8 - event[1]
11431197
if 0 <= x < 8 and 0 <= y < 8:
1144-
self.reset_launchpad_light(x, y, launchpad=lp)
1198+
# x2, y2 = self.untransform_launchpad_coord(lp.index, x, y)
1199+
# self.reset_launchpad_light(x2, y2, launchpad=lp, transform=False)
11451200
if not self.is_macro_button(x, 8 - y - 1):
1201+
x, y = self.transform_launchpad_coord(lp.index, x, y)
11461202
note = y * 8 + x
11471203
self.note_off([128, note, event[2]], timestamp, width=8, transpose=lp.transpose, octave=lp.get_octave(), force_channel=self.options.launchpad_channel)
11481204
else:
@@ -1154,8 +1210,10 @@ def cb_launchpad_in(self, lp, event, timestamp=0):
11541210
x = event[0]
11551211
y = 8 - event[1]
11561212
if 0 <= x < 8 and 0 <= y < 8:
1157-
self.set_launchpad_light(x, y, -1, launchpad=lp)
1213+
# x2, y2 = self.untransform_launchpad_coord(lp.index, x, y)
1214+
# self.set_launchpad_light(x2, y2, -1, launchpad=lp, transform=False)
11581215
if not self.is_macro_button(x, 8 - y - 1):
1216+
x, y = self.transform_launchpad_coord(lp.index, x, y)
11591217
note = y * 8 + x
11601218
self.note_on([144, note, event[2]], timestamp, width=8, transpose=lp.transpose, octave=lp.get_octave(), force_channel=self.options.launchpad_channel)
11611219
else:
@@ -1300,6 +1358,9 @@ def __init__(self):
13001358
self.options.colors = list(self.options.colors.split(","))
13011359
self.options.colors = list(map(lambda x: glm.ivec3(get_color(x)), self.options.colors))
13021360

1361+
self.options.swap_launchpads = get_option(opts, "swap_launchpads", self.options.swap_launchpads)
1362+
self.options.rotate_launchpads = get_option(opts, "rotate_launchpads", self.options.rotate_launchpads)
1363+
13031364
self.options.launchpad_colors = get_option(opts, "launchpad_colors", DEFAULT_OPTIONS.launchpad_colors)
13041365
if self.options.launchpad_colors:
13051366
self.options.launchpad_colors = list(self.options.launchpad_colors.split(","))
@@ -1349,6 +1410,10 @@ def __init__(self):
13491410
opts, "lite", DEFAULT_OPTIONS.lite
13501411
)
13511412

1413+
self.options.rpn_delay = get_option(
1414+
opts, "rpn_delay", DEFAULT_OPTIONS.rpn_delay
1415+
)
1416+
13521417
# bend the velocity curve, examples: 0.5=sqrt, 1.0=default, 2.0=squared
13531418
self.options.velocity_curve = get_option(
13541419
opts, "velocity_curve", DEFAULT_OPTIONS.velocity_curve
@@ -1544,6 +1609,7 @@ def __init__(self):
15441609
pygame.display.set_caption(TITLE)
15451610
self.icon = pygame.image.load('icon.png')
15461611
pygame.display.set_icon(self.icon)
1612+
pygame.display.set_allow_screensaver(True)
15471613
# if FOCUS:
15481614
# pygame.mouse.set_visible(0)
15491615
# pygame.event.set_grab(True)
@@ -1780,8 +1846,11 @@ def __init__(self):
17801846
self.launchpads += [Launchpad(self, lp, "lpx", num_launchpads, self.options.octave_separation)]
17811847
num_launchpads += 1
17821848

1783-
if self.launchpads:
1784-
print('Launchpads:', len(self.launchpads))
1849+
# if self.launchpads:
1850+
# launchpad_count = len(self.launchpads)
1851+
# print('Launchpads:', launchpad_count)
1852+
# if launchpad_count >= 2 and self.options.swap_launchpads:
1853+
# self.launchpads = self.launchpads[::-1]
17851854

17861855
self.done = False
17871856

@@ -1823,6 +1892,8 @@ def __init__(self):
18231892
self.setup_rpn()
18241893
# self.test()
18251894

1895+
# msgbox("Welcome to midimech!\n\nThis project is still in development and some features are experimental. Feel free to play around and report any bugs to flipcoder. Thanks!", "midimech")
1896+
18261897
def midi_mode_rpn(self, on=True):
18271898
if on:
18281899
self.rpn(0, 1 if self.is_mpe() else 0)
@@ -1875,7 +1946,7 @@ def rpn(self, num, value):
18751946
self.midi_write(self.linn_out, [176, 38, value_lsb])
18761947
self.midi_write(self.linn_out, [176, 101, 127])
18771948
self.midi_write(self.linn_out, [176, 100, 127])
1878-
time.sleep(0.05)
1949+
time.sleep(self.options.rpn_delay)
18791950

18801951
def mpe_rpn(self, on=True):
18811952
"""Sets up MPE settings (except MIDI mode)"""
@@ -2354,7 +2425,7 @@ def render(self):
23542425

23552426
self.screen.surface.fill((0, 0, 0))
23562427
b = 2 # border
2357-
sz = self.screen_w / self.board_w
2428+
sz = self.scale.x
23582429
y = 0
23592430
rad = int(sz // 2 - 8)
23602431

src/launchpad.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ def __init__(self, core, out, mode, index=0, octave_separation=0):
88
self.mode = mode
99
self.index = index
1010
self.octave_separation = octave_separation
11+
self.rot = False
12+
if core.options.rotate_launchpads:
13+
if core.options.swap_launchpads:
14+
self.rot = not bool(index)
15+
else:
16+
self.rot = bool(index)
1117

1218
print("Launchpad", mode, 'Connected! (#' + str(index) + ")")
1319

14-
# self.pos = glm.ivec2(0, 0)
15-
1620
def button(self, x, y):
1721
# if self.mode == 'lpx':
1822
if y == -1:
@@ -80,7 +84,7 @@ def button(self, x, y):
8084
self.core.prev_program()
8185

8286
def set_lights(self):
83-
# if self.mode == "lpx":
87+
8488
self.out.LedCtrlXY(0, 0, 0, 0, 63)
8589
self.out.LedCtrlXY(1, 0, 0, 0, 63)
8690
self.out.LedCtrlXY(2, 0, 63, 0, 63)

0 commit comments

Comments
 (0)