Godtone (talk | contribs)
m Isomorphic keyboard code for launchpads with programmer mode: add option to use backslashes in printed .scl file and use absolute value of x and y to correctly deduce needing a large layout when a small layout isnt forced
Godtone (talk | contribs)
Isomorphic keyboard code for launchpads with programmer mode: add octave/period-(y_)reducer (subtraction rather) every(_x) / every_y notes feature, add 80-colour handcrafted rainbow and make 16, 20 and 40 notes exist as subsets. also allow for specifying the bg colour when highlighting a scale/subset
Line 1,573: Line 1,573:
rb25 = [c for c in rb26 if c!=74]
rb25 = [c for c in rb26 if c!=74]


colmaps = { 12: rb12, 13: rb13, 16: cm16, 17: rb17v2, 18: rb18, 19: rb19, 25: rb25, 26: rb26, 27: rb27, 28: rb28, 29: rb29, 30: rb30, 31: rb31, 32: rb32, 33: rb33, 34: rb34, 35: rb35, 36: rb36, 37: rb37 }
colmaps = { 12: rb12, 13: rb13, 17: rb17v2, 18: rb18, 19: rb19, 25: rb25, 26: rb26, 27: rb27, 28: rb28, 29: rb29, 30: rb30, 31: rb31, 32: rb32, 33: rb33, 34: rb34, 35: rb35, 36: rb36, 37: rb37 }
# IMPORTANT: SUSPECTED DUPLICATE COLOURS: 45 = 67, 97 = 124, (a duplicate red i need to refind)
# IMPORTANT: SUSPECTED DUPLICATE COLOURS: 45 = 67, 97 = 124, (a duplicate red i need to refind)
rb80map = {0: 2, 1: 83, 2: 8, 3: 47, 4: 46, 5: 55, 6: 51, 7: 81, 8: 36, 9: 105, 10: 1, 11: 111, 12: 99, 13: 9, 14: 116, 15: 66, 16: 119, 17: 40, 18: 44, 19: 78, 20: 5, 21: 45, 22: 65, 23: 70, 24: 124, 25: 98, 26: 21, 27: 4, 28: 29, 29: 33, 30: 13, 31: 113, 32: 3, 33: 115, 34: 20, 35: 102, 36: 104, 37: 53, 38: 54, 39: 55, 40: 2}
rb80 = [rb80map[i] for i in range(41)]
rb80 = rb80 + rb80[::-1][1:-1]
colmaps[80] = rb80
colmaps[40] = rb80[::2]
colmaps[20] = rb80[::4]
colmaps[16] = rb80[::5]




Line 1,639: Line 1,646:
# use the same midi/mido output (mo) for the entire session: (the port is opened when you import the library)
# use the same midi/mido output (mo) for the entire session: (the port is opened when you import the library)
mo = mido.open_output() if platform.system()!='Windows' else mido.open_output([ port for port in mido.get_output_names() if 'loop' in port ][0])
mo = mido.open_output() if platform.system()!='Windows' else mido.open_output([ port for port in mido.get_output_names() if 'loop' in port ][0])


# IMPORTANT: to highlight a scale, use [a,b,c,...,p] where p is the number of MIDI notes corresponding to your period and a>0.
# IMPORTANT: to highlight a scale, use [a,b,c,...,p] where p is the number of MIDI notes corresponding to your period and a>0.
# SEE: genindices() to generate a rank 2 scale and mode() to rotate an arbitrary scale.
# SEE: genindices() to generate a rank 2 scale and mode() to rotate an arbitrary scale.
def iso(x=5, y=1, colmap_in=[34], highlight=[], **args):
def iso(x=5, y=1, colmap_in=[34], highlight=[], **args):
autorelative = args.get('autorelative') or False
bg = args.get('bg') # colour to indicate note is not in the highlighted subset (white by default)
relative = args.get('relative') or autorelative
if bg==None: # default
bg = 3
relative = args.get('relative') or args.get('rel') or False
autorelative = args.get('autorelative') or args.get('autorel') or False
last_tuning_note = 0
last_tuning_note = 0
global mo
global mo
Line 1,673: Line 1,682:
# these are for if you want or need to keep the colour mapping distinct from the midi notes EG in large EDO subsets
# these are for if you want or need to keep the colour mapping distinct from the midi notes EG in large EDO subsets
large = args.get('large') or (not args.get('small') and max(abs(x)*9+abs(y)*8,abs(x)*8+abs(y)*9) > 127) or False
large = args.get('large') or (not args.get('small') and max(abs(x)*9+abs(y)*8,abs(x)*8+abs(y)*9) > 127) or False
# the idea of this feature is to reduce by an octave
# (or more generally some abstract interval of [reducer] MIDI notes)
# and to do so every [every] notes.
# reducer,every (equiv. x_reducer,every_x) specify x axis.
# y_reducer,every_y specify y axis.
# DOES NOT WORK WITH LARGE (large=True) LAYOUTS CURRENTLY.
x_reducer = args.get('x_reducer') or args.get('reducer') or 0
every_x = args.get('every_x') or args.get('every') or 2
y_reducer = args.get('y_reducer') or 0
every_y = args.get('every_y') or 2
coverage = 10 # probably shouldn't mess with this unless you know what you're doing
coverage = 10 # probably shouldn't mess with this unless you know what you're doing
if not large and (x < 0 or y < 0):
print('WARNING: using a negative number of MIDI notes per increment of the x or y axis likely requires setting the bottomleftnote bl correspondingly')
if large:
if large:
print('WARNING: the midi note range of your layout is too large, so x=1, y='+str(coverage)+'=c=coverage is being used.')
print('WARNING: the midi note range of your layout is too large, so x=1, y='+str(coverage)+'=c=coverage is being used.')
Line 1,685: Line 1,707:
for i in range(coverage):
for i in range(coverage):
if j!=0 or i!=0: # equiv. if (i,j) != (0,0) (i think)
if j!=0 or i!=0: # equiv. if (i,j) != (0,0) (i think)
print( str(i*x + j*y)+'\\'+str(edo) if args.get('backslash') else int(.5 + 10000 * (i*x + j*y)/edo*1200)/10000 )
steps = i*x - i//every_x*x_reducer + j*y - j//every_y*y_reducer
print( str(steps)+'\\'+str(edo) if args.get('backslash') or args.get('backslashes') else int(.5 + 10000 * steps/edo*1200)/10000 )
print()
print()
# for the launchpad pro MK3 specifically:
# for the launchpad pro MK3 specifically:
Line 1,697: Line 1,720:
                 highlight = sorted(list(set(highlight)))
                 highlight = sorted(list(set(highlight)))
                 mul = math.lcm(len(colmap),highlight[-1])
                 mul = math.lcm(len(colmap),highlight[-1])
                 colmap = [( colmap*mul )[i%len(colmap)] if i%highlight[-1] in [0]+highlight else 3 for i in range(mul)]
                 colmap = [( colmap*mul )[i%len(colmap)] if i%highlight[-1] in [0]+highlight else bg for i in range(mul)]
# find launchpad output (what we send colour (col) data to)
# find launchpad output (what we send colour (col) data to)
Line 1,723: Line 1,746:
def transform(midi_note):
def transform(midi_note):
if midi_note < 100:
if midi_note < 100:
return midi_note%10 * x + midi_note//10 * y + bottomleftnote + (10 if extra else 0)
return midi_note%10 * x - (midi_note%10)//every_x*x_reducer + midi_note//10 * y - (midi_note//10)//every_y*y_reducer + bottomleftnote + (10 if extra else 0)
else:
else:
return (midi_note - 100) * x + bottomleftnote
return (midi_note - 100) * x - (midi_note%10)//every_x*x_reducer + bottomleftnote
def get_midi_note(midi_note):
def get_midi_note(midi_note):
if extra and midi_note < 10:
if extra and midi_note < 10: