@@ -454,16 +454,20 @@ def next_free_note(self):
454454 def is_mpe (self ):
455455 return self .options .one_channel == 0
456456
457- def note_on (self , data , timestamp , width = None , curve = True , mpe = None ):
457+ def note_on (self , data , timestamp , width = None , curve = True , mpe = None , force_channel = None ):
458458 # if mpe is None:
459459 # mpe = self.options.mpe
460460 d0 = data [0 ]
461461 # print(data)
462462 ch = d0 & 0x0F
463463 msg = (data [0 ] & 0xF0 ) >> 4
464464 aftertouch = (msg == 10 )
465- if not self .is_mpe ():
465+
466+ if force_channel :
467+ data [0 ] = (d0 & 0xF0 ) + (force_channel - 1 )
468+ elif not self .is_mpe ():
466469 data [0 ] = (d0 & 0xF0 ) + (self .options .one_channel - 1 )
470+
467471 row = None
468472 col = None
469473
@@ -500,6 +504,7 @@ def note_on(self, data, timestamp, width=None, curve=True, mpe=None):
500504 # if mpe:
501505 row = data [1 ] // width
502506 col = data [1 ] % width
507+ col_full = col + (left_width if within_hardware_split else 0 )
503508 if within_hardware_split :
504509 data [1 ] += left_width
505510 data [1 ] = col + 30 + 2.5 * row
@@ -520,7 +525,7 @@ def note_on(self, data, timestamp, width=None, curve=True, mpe=None):
520525 data [1 ] += (self .octave + self .octave_base ) * 12
521526 data [1 ] += BASE_OFFSET
522527 midinote = data [1 ] - 24 + self .transpose * 2
523- side = self .channel_from_split (col , row , force = True )
528+ side = self .channel_from_split (col_full , row , force = True )
524529 if self .is_split ():
525530 split_chan = side
526531 else :
@@ -579,16 +584,18 @@ def note_on(self, data, timestamp, width=None, curve=True, mpe=None):
579584 else :
580585 self .midi_write (self .midi_out , data , timestamp )
581586
582- def note_off (self , data , timestamp , width = None , mpe = None ):
587+ def note_off (self , data , timestamp , width = None , mpe = None , force_channel = None ):
583588 # if mpe is None:
584589 # mpe = self.options.mpe
585590
586591 d0 = data [0 ]
587592 # print(data)
588593 ch = d0 & 0x0F
589594 msg = (data [0 ] & 0xF0 ) >> 4
590- if not self .is_mpe ():
591- data [0 ] = (d0 & 0xF0 ) | (self .options .one_channel - 1 )
595+ if force_channel :
596+ data [0 ] = (d0 & 0xF0 ) + (force_channel - 1 )
597+ elif not self .is_mpe ():
598+ data [0 ] = (d0 & 0xF0 ) + (self .options .one_channel - 1 )
592599 row = None
593600 col = None
594601
@@ -639,6 +646,7 @@ def note_off(self, data, timestamp, width=None, mpe=None):
639646 # data[1] += self.board_w
640647 row = data [1 ] // width
641648 col = data [1 ] % width
649+ col_full = col + (left_width if within_hardware_split else 0 )
642650 if within_hardware_split :
643651 data [1 ] += left_width
644652 data [1 ] = col + 30 + 2.5 * row
@@ -656,7 +664,7 @@ def note_off(self, data, timestamp, width=None, mpe=None):
656664 data [1 ] += (self .octave + self .octave_base ) * 12
657665 data [1 ] += BASE_OFFSET
658666 midinote = data [1 ] - 24 + self .transpose * 2
659- side = self .channel_from_split (col , row , force = True )
667+ side = self .channel_from_split (col_full , row , force = True )
660668 if self .is_split ():
661669 split_chan = side
662670 else :
@@ -687,14 +695,16 @@ def note_off(self, data, timestamp, width=None, mpe=None):
687695 self .midi_write (self .midi_out , data , timestamp )
688696 # print('note off: ', data)
689697
690- def device_to_xy (self , data ):
698+ def device_to_xy (self , data , force_channel = None ):
691699 # mpe = self.options.mpe
692700
693701 d0 = data [0 ]
694702 ch = d0 & 0x0F
695703 msg = (data [0 ] & 0xF0 ) >> 4
696- if not self .is_mpe ():
697- data [0 ] = (d0 & 0xF0 ) | (self .options .one_channel - 1 )
704+ if force_channel :
705+ data [0 ] = (d0 & 0xF0 ) + (force_channel - 1 )
706+ elif not self .is_mpe ():
707+ data [0 ] = (d0 & 0xF0 ) + (self .options .one_channel - 1 )
698708 row = None
699709 col = None
700710
@@ -741,7 +751,7 @@ def device_to_xy(self, data):
741751
742752 return col , row
743753
744- def cb_midi_in (self , data , timestamp ):
754+ def cb_midi_in (self , data , timestamp , force_channel = None ):
745755 """LinnStrument MIDI Callback"""
746756 # d4 = None
747757 # if len(data)==4:
@@ -751,8 +761,6 @@ def cb_midi_in(self, data, timestamp):
751761 # print(data)
752762 ch = d0 & 0x0F
753763 msg = (data [0 ] & 0xF0 ) >> 4
754- if not self .is_mpe ():
755- data [0 ] = (d0 & 0xF0 ) | (self .options .one_channel - 1 )
756764 row = None
757765 col = None
758766 # if not self.options.mpe:
@@ -767,9 +775,16 @@ def cb_midi_in(self, data, timestamp):
767775 elif msg == 8 : # note off
768776 self .note_off (data , timestamp )
769777 elif 0xF0 <= msg <= 0xF7 : # sysex
778+ # rewrite the output channel based on app's MPE settings
770779 self .midi_write (self .midi_out , data , timestamp )
771780 else :
772781 # pitch bend
782+ # rewrite the output channel based on app's MPE settings
783+ if force_channel :
784+ data [0 ] = (d0 & 0xF0 ) + (force_channel - 1 )
785+ elif not self .is_mpe ():
786+ data [0 ] = (d0 & 0xF0 ) + (self .options .one_channel - 1 )
787+
773788 skip = False
774789 if msg == 14 :
775790 if self .is_split ():
@@ -894,7 +909,7 @@ def cb_launchpad_in(self, event, timestamp=0):
894909 vel = event [2 ]
895910 note = y * 8 + x
896911 if not self .is_macro_button (x , 8 - y - 1 ):
897- self .note_on ([160 , note , event [2 ]], timestamp , width = 8 )
912+ self .note_on ([160 , note , event [2 ]], timestamp , width = 8 , force_channel = self . options . launchpad_channel )
898913 self .articulation .pressure (vel / 127 )
899914 else :
900915 self .macro (x , 8 - y - 1 , vel / 127 )
@@ -905,7 +920,7 @@ def cb_launchpad_in(self, event, timestamp=0):
905920 self .reset_launchpad_light (x , y )
906921 if not self .is_macro_button (x , 8 - y - 1 ):
907922 note = y * 8 + x
908- self .note_off ([128 , note , event [2 ]], timestamp , width = 8 )
923+ self .note_off ([128 , note , event [2 ]], timestamp , width = 8 , force_channel = self . options . launchpad_channel )
909924 else :
910925 self .macro (x , 8 - y - 1 , False )
911926 else : # note on
@@ -915,7 +930,7 @@ def cb_launchpad_in(self, event, timestamp=0):
915930 note = y * 8 + x
916931 self .set_launchpad_light (x , y , 0 ) # red
917932 if not self .is_macro_button (x , 8 - y - 1 ):
918- self .note_on ([144 , note , event [2 ]], timestamp , width = 8 )
933+ self .note_on ([144 , note , event [2 ]], timestamp , width = 8 , force_channel = self . options . launchpad_channel )
919934 else :
920935 self .macro (x , 8 - y - 1 , True )
921936 else :
@@ -1157,14 +1172,20 @@ def __init__(self):
11571172 print ("Invalid sustain split value. Options: left, right, both." )
11581173 sys .exit (1 )
11591174
1175+ hardware_split = False
11601176 self .options .size = get_option (opts , "size" , DEFAULT_OPTIONS .size )
11611177 if self .options .size == 128 :
11621178 self .options .width = 16
11631179 elif self .options .size == 200 :
11641180 self .options .width = 25
1165- self .options .hardware_split = True
1181+ hardware_split = True
1182+
1183+ # Note: The default below is what is determined by size above.
1184+ # Overriding hardware_split is only useful for 128 user testing 200 behavior
1185+ self .options .hardware_split = get_option (opts , "hardware_split" , hardware_split )
11661186
11671187 self .options .launchpad = get_option (opts , 'launchpad' , True )
1188+ self .options .launchpad_channel = get_option (opts , 'launchpad_channel' , 1 )
11681189 self .options .experimental = get_option (opts , 'experimental' , False )
11691190 self .options .debug = get_option (opts , 'debug' , False )
11701191 self .options .stabilizer = get_option (opts , 'stabilizer' , False )
0 commit comments