85
85
86
86
class Note :
87
87
def __init__ (self ):
88
- self .bend = 0.0
89
- self .pressed = False
90
- self .intensity = 0.0 # how much to light marker up
88
+ # self.bend = 0.0
89
+ # self.pressed = False
90
+ # self.intensity = 0.0 # how much to light marker up (in app)
91
91
self .pressure = 0.0 # how much the note is being pressed
92
- self .dirty = False
92
+ # self.dirty = False
93
93
self .location : ivec2 = None # on board
94
+ self .midinote = None
94
95
95
96
# def logic(self, dt):
96
97
# if self.pressed: # pressed, fade to pressure value
@@ -257,15 +258,8 @@ def get_color(self, x, y):
257
258
def mouse_held (self ):
258
259
return self .mouse_midi != - 1
259
260
260
- def mouse_press (self , x , y , state = True , hold = False ):
261
- if y < 0 :
262
- return
263
-
264
- # if we're not intending to hold the note, we release the previous primary note
265
- if not hold :
266
- if self .mouse_held ():
267
- self .mouse_release ()
268
-
261
+ # layout button x, y and velocity
262
+ def mouse_pos_to_press (self , x , y ):
269
263
vel = y % int (self .button_sz )
270
264
x /= int (self .button_sz )
271
265
y /= int (self .button_sz )
@@ -276,13 +270,53 @@ def mouse_press(self, x, y, state=True, hold=False):
276
270
vel = clamp (0 , 127 , int (vel ))
277
271
278
272
x , y = int (x ), int (y )
273
+ return (x , y , vel )
279
274
280
- self .mark_xy (x , y , state )
275
+ def mouse_press (self , x , y , state = True , hold = False , hover = False ):
276
+ if y < 0 :
277
+ return
278
+
279
+ if hover :
280
+ btn = pygame .mouse .get_pressed (3 )[0 ]
281
+ if not btn :
282
+ return
283
+
284
+ # if we're not intending to hold the note, we release the previous primary note
285
+ if not hover :
286
+ if self .mouse_held ():
287
+ self .mouse_release ()
288
+
289
+ x , y , vel = self .mouse_pos_to_press (x , y )
290
+
291
+ if hover and self .mouse_midi_vel is not None :
292
+ # if hovering, get velocity of last click
293
+ vel = self .mouse_midi_vel
294
+ if not hover and self .mouse_midi_vel is None :
295
+ self .mouse_midi_vel = vel # store velocity for initial click
296
+
297
+ # vel = y % int(self.button_sz)
298
+ # x /= int(self.button_sz)
299
+ # y /= int(self.button_sz)
300
+
301
+ # vel = vel / int(self.button_sz)
302
+ # vel = 1 - vel
303
+ # vel *= 127
304
+ # vel = clamp(0, 127, int(vel))
305
+
306
+ # x, y = int(x), int(y)
281
307
v = ivec2 (x , y )
308
+
309
+ self .mark_xy (x , y , state )
282
310
midinote = self .xy_to_midi (v .x , v .y )
311
+ if hover :
312
+ if self .mouse_midi == midinote :
313
+ return
314
+ else :
315
+ self .mouse_release ()
283
316
if not hold :
284
317
self .mouse_mark = v
285
318
self .mouse_midi = midinote
319
+ self .mouse_midi_vel = vel
286
320
data = [0x90 if state else 0x80 , midinote , vel ]
287
321
if self .midi_out :
288
322
self .midi_write (self .midi_out , data , 0 )
@@ -302,6 +336,9 @@ def mouse_release(self, x=None, y=None):
302
336
self .midi_write (self .midi_out , data , 0 )
303
337
self .mouse_midi = - 1
304
338
339
+ def mouse_hover (self , x , y ):
340
+ self .mouse_press (x , y , hover = True )
341
+
305
342
# Given an x,y position, find the octave
306
343
# (used to initialize octaves 2D array)
307
344
def init_octave (self , x , y ):
@@ -336,6 +373,12 @@ def midi_write(self, dev, msg, ts=0):
336
373
if dev :
337
374
dev .send_raw (* msg )
338
375
376
+ def next_free_note (self ):
377
+ for note in self .notes :
378
+ if note .location is None :
379
+ return note
380
+ return None
381
+
339
382
def note_on (self , data , timestamp , width = None , curve = True , no_overlap = None ):
340
383
if width is None :
341
384
width = self .board_w // 2 if self .options .hardware_split else self .board_w
@@ -350,9 +393,6 @@ def note_on(self, data, timestamp, width=None, curve=True, no_overlap=None):
350
393
data [0 ] = d0 & 0xF0 # send all to channel 0 if enabled
351
394
row = None
352
395
col = None
353
- if not no_overlap :
354
- row = ch % 8
355
- col = ch // 8
356
396
357
397
if no_overlap :
358
398
row = data [1 ] // width
@@ -363,6 +403,8 @@ def note_on(self, data, timestamp, width=None, curve=True, no_overlap=None):
363
403
if self .options .hardware_split and ch >= 8 :
364
404
data [1 ] += width * 2
365
405
else :
406
+ row = ch % 8
407
+ col = ch // 8
366
408
data [1 ] *= 2
367
409
try :
368
410
data [1 ] -= row * 5
@@ -392,12 +434,22 @@ def note_on(self, data, timestamp, width=None, curve=True, no_overlap=None):
392
434
int (vel * 127 + 0.5 ),
393
435
)
394
436
395
- note = self .notes [ch ]
396
- if note .location is None :
397
- note .location = ivec2 (0 )
398
- self .notes [ch ].location .x = col
399
- self .notes [ch ].location .y = row
400
- self .notes [ch ].pressure = vel
437
+ if aftertouch :
438
+ # TODO: add aftertouch values into notes array
439
+ # This is not necessary yet
440
+ pass
441
+ else :
442
+ if self .options .no_overlap :
443
+ note = self .notes [ch ]
444
+ else :
445
+ note = self .next_free_note ()
446
+ if note :
447
+ if note .location is None :
448
+ note .location = ivec2 (0 )
449
+ note .location .x = col
450
+ note .location .y = row
451
+ note .pressure = vel
452
+ note .midinote = data [1 ]
401
453
402
454
if self .is_split ():
403
455
if split_chan == 0 :
@@ -538,32 +590,70 @@ def cb_foot(self, data, timestamp):
538
590
high = self .options .velocity_curve_high
539
591
self .velocity_curve_ = low + val2 * (high - low )
540
592
541
- def cb_launchpad_in ( self , event , timestamp ):
542
- if event [ 0 ] == 144 :
543
- # convert to x, y (lower left is 0, 0)
544
- y = event [1 ] // 10 - 1
545
- x = event [ 1 ] % 10 - 1
546
- # convert it to no overlap chromatic
547
-
548
- self . launchpad_state [ y ][ x ] = None
549
- note = y * 8 + x
550
- self .note_off ([ 128 , note , event [ 2 ] ], timestamp , width = 8 , no_overlap = True )
551
- elif event [ 0 ] == 160 :
552
- y = event [1 ] // 10 - 1
553
- x = event [1 ] % 10 - 1
554
- state = self . launchpad_state [ y ][ x ]
555
- self . launchpad_state [ y ][ x ] = event [2 ]
593
+ # uses button state events (mk3 pro)
594
+ def cb_launchpad_in ( self , event , timestamp = 0 ) :
595
+ # if (self.launchpad_mode == "pro" or self.launchpad_mode == "promk3") and event[0] == 255:
596
+ # if event[0] >= 255:
597
+ # # I'm testing the mk3 method on an lpx, so I'll check this here
598
+ # vel = event[2] if self.launchpad_mode == 'lpx' else event[1]
599
+ # for note in self.notes:
600
+ # if note.location:
601
+ # print(note.midinote)
602
+ # self.note_on([160 , note.midinote, vel ], timestamp, width=8, no_overlap=True)
603
+
604
+ if self . launchpad_mode == 'lpx' and event [0 ] >= 255 : # pressure
605
+ x = event [0 ] - 255
606
+ y = 8 - ( event [ 1 ] - 255 )
607
+ vel = event [2 ]
556
608
note = y * 8 + x
557
- if state is None : # just pressed
558
- self .note_on ([144 , note , event [2 ]], timestamp , width = 8 , no_overlap = True , curve = False )
559
- self .note_on ([160 , note , event [2 ]], timestamp , width = 8 , no_overlap = True , curve = False )
560
- elif event [0 ] == 176 :
561
- if event == [176 , 93 , 127 , 0 ]:
562
- self .transpose_board (- 1 )
563
- self .dirty = self .dirty_lights = True
564
- elif event == [176 , 94 , 127 , 0 ]:
565
- self .transpose_board (1 )
566
- self .dirty = self .dirty_lights = True
609
+ self .note_on ([160 , note , event [2 ]], timestamp , width = 8 , no_overlap = True )
610
+ elif event [2 ] == 0 : # note off
611
+ x = event [0 ]
612
+ y = 8 - event [1 ]
613
+ if 0 <= x < 8 and 0 <= y < 8 :
614
+ note = y * 8 + x
615
+ self .note_off ([128 , note , event [2 ]], timestamp , width = 8 , no_overlap = True )
616
+ else : # note on
617
+ x = event [0 ]
618
+ y = 8 - event [1 ]
619
+ if 0 <= x < 8 and 0 <= y < 8 :
620
+ note = y * 8 + x
621
+ self .note_on ([144 , note , event [2 ]], timestamp , width = 8 , no_overlap = True )
622
+ else :
623
+ if x == 2 :
624
+ self .transpose_board (- 1 )
625
+ self .dirty = self .dirty_lights = True
626
+ elif x == 3 :
627
+ self .transpose_board (1 )
628
+ self .dirty = self .dirty_lights = True
629
+
630
+ # uses raw events (Launchpad X)
631
+ # def cb_launchpad_in(self, event, timestamp=0):
632
+ # if event[0] == 144:
633
+ # # convert to x, y (lower left is 0, 0)
634
+ # y = event[1] // 10 - 1
635
+ # x = event[1] % 10 - 1
636
+ # # convert it to no overlap chromatic
637
+
638
+ # self.launchpad_state[y][x] = None
639
+ # note = y * 8 + x
640
+ # self.note_off([128, note, event[2]], timestamp, width=8, no_overlap=True)
641
+ # elif event[0] == 160:
642
+ # y = event[1] // 10 - 1
643
+ # x = event[1] % 10 - 1
644
+ # state = self.launchpad_state[y][x]
645
+ # self.launchpad_state[y][x] = event[2]
646
+ # note = y * 8 + x
647
+ # if state is None: # just pressed
648
+ # self.note_on([144, note, event[2]], timestamp, width=8, no_overlap=True, curve=False)
649
+ # self.note_on([160, note, event[2]], timestamp, width=8, no_overlap=True, curve=False)
650
+ # elif event[0] == 176:
651
+ # if event == [176, 93, 127, 0]:
652
+ # self.transpose_board(-1)
653
+ # self.dirty = self.dirty_lights = True
654
+ # elif event == [176, 94, 127, 0]:
655
+ # self.transpose_board(1)
656
+ # self.dirty = self.dirty_lights = True
567
657
568
658
# if events[0] >= 255:
569
659
# print("PRESSURE: " + str(events[0]-255) + " " + str(events[1]))
@@ -702,6 +792,7 @@ def __init__(self):
702
792
self .options .width = get_option (opts , "width" , DEFAULT_OPTIONS .width )
703
793
704
794
self .options .launchpad = get_option (opts , 'launchpad' , True )
795
+ self .options .experimental = get_option (opts , 'experimental' , False )
705
796
706
797
# simulator keys
707
798
self .keys = {}
@@ -755,6 +846,9 @@ def __init__(self):
755
846
756
847
self .mouse_mark = ivec2 (0 )
757
848
self .mouse_midi = - 1
849
+ self .mouse_midi_vel = None
850
+
851
+ self .last_note = None # ivec2
758
852
759
853
self .init_board ()
760
854
@@ -900,6 +994,7 @@ def __init__(self):
900
994
self .midi_in = None
901
995
self .visualizer = None
902
996
self .launchpad = None
997
+ self .launchpad_mode = None
903
998
904
999
innames = rtmidi2 .get_in_ports ()
905
1000
for i in range (len (innames )):
@@ -922,22 +1017,23 @@ def __init__(self):
922
1017
self .foot_in .callback = self .cb_foot
923
1018
924
1019
if self .options .launchpad :
925
- # lp = launchpad.LaunchpadPro()
926
- # if lp.Check(0):
927
- # if lp.Open(0):
928
- # mode = "pro"
929
- # elif launchpad.LaunchpadProMk3().Check(0):
930
- # lp = launchpad.LaunchpadProMk3()
931
- # if lp.Open(0):
932
- # mode = "promk3"
933
- # elif
934
- if launchpad .LaunchpadLPX ().Check (1 ):
935
- lp = launchpad .LaunchpadLPX ()
936
- if lp .Open (1 ):
937
- mode = "lpx"
938
- if mode is not None :
1020
+ lp = None
1021
+ if self .options .experimental :
1022
+ lp = launchpad .LaunchpadPro ()
1023
+ if lp .Check (0 ):
1024
+ if lp .Open (0 ):
1025
+ self .launchpad_mode = "pro"
1026
+ if launchpad .LaunchpadProMk3 ().Check (0 ):
1027
+ lp = launchpad .LaunchpadProMk3 ()
1028
+ if lp .Open (0 ):
1029
+ self .launchpad_mode = "promk3"
1030
+ if not self .launchpad_mode :
1031
+ if launchpad .LaunchpadLPX ().Check (1 ):
1032
+ lp = launchpad .LaunchpadLPX ()
1033
+ if lp .Open (1 ):
1034
+ self .launchpad_mode = "lpx"
1035
+ if self .launchpad_mode is not None :
939
1036
self .launchpad = lp
940
- # self.init_launchpad()
941
1037
942
1038
self .done = False
943
1039
@@ -1020,7 +1116,10 @@ def mark_xy(self, x, y, state, use_lights=False):
1020
1116
def mark (self , midinote , state , use_lights = False , only_row = None ):
1021
1117
if only_row is not None :
1022
1118
only_row = self .board_h - only_row - 1 - self .flipped # flip
1023
- rows = [self .board [only_row ]]
1119
+ try :
1120
+ rows = [self .board [only_row ]]
1121
+ except IndexError :
1122
+ rows = self .board
1024
1123
y = only_row
1025
1124
else :
1026
1125
rows = self .board
@@ -1068,11 +1167,17 @@ def logic(self, dt):
1068
1167
# keys = pygame.key.get_pressed()
1069
1168
1070
1169
if self .launchpad :
1170
+ # while True:
1171
+ # events = self.launchpad.EventRaw()
1172
+ # if events != []:
1173
+ # for ev in events:
1174
+ # self.cb_launchpad_in(ev[0], ev[1])
1175
+ # else:
1176
+ # break
1071
1177
while True :
1072
- events = self .launchpad .EventRaw ()
1073
- if events != []:
1074
- for ev in events :
1075
- self .cb_launchpad_in (ev [0 ], ev [1 ])
1178
+ event = self .launchpad .ButtonStateXY (returnPressure = True )
1179
+ if event :
1180
+ self .cb_launchpad_in (event )
1076
1181
else :
1077
1182
break
1078
1183
@@ -1107,6 +1212,10 @@ def logic(self, dt):
1107
1212
self .midi_write (self .midi_out , data , 0 )
1108
1213
except KeyError :
1109
1214
pass
1215
+ elif ev .type == pygame .MOUSEMOTION :
1216
+ x , y = ev .pos
1217
+ y -= self .menu_sz
1218
+ self .mouse_hover (x , y )
1110
1219
elif ev .type == pygame .MOUSEBUTTONDOWN :
1111
1220
x , y = ev .pos
1112
1221
y -= self .menu_sz
0 commit comments