first commit Live launcher code

This commit is contained in:
John Park 2021-07-07 14:34:11 -07:00
parent c6cdb94ecb
commit fa4375a281

252
Macropad_Ableton/code.py Normal file
View file

@ -0,0 +1,252 @@
# SPDX-FileCopyrightText: 2021 John Park for Adafruit Industries
# SPDX-License-Identifier: MIT
# Ableton Live Macropad Launcher
# In Ableton, choose "Launchpad Mini Mk3" as controller with MacroPad 2040 as in and out
import board
from digitalio import DigitalInOut, Pull
import keypad
import displayio
import terminalio
import neopixel
import rotaryio
from adafruit_simplemath import constrain
from adafruit_display_text import label
from adafruit_debouncer import Debouncer
import usb_midi
import adafruit_midi
from adafruit_midi.control_change import ControlChange
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
from adafruit_midi.midi_message import MIDIUnknownEvent
TITLE_TEXT = "Live Launcher 2040"
print(TITLE_TEXT)
TRACK_NAMES = ["DRUM", "BASS", "SYNTH"] # Customize these
LIVE_CC_NUMBER = 74 # CC number to send w encoder
FADER_TEXT = "cutoff" # change for intended CC name
midi = adafruit_midi.MIDI(
midi_in=usb_midi.ports[0],
in_channel=(0, 1, 2),
midi_out=usb_midi.ports[1],
out_channel=0
)
# ---Specify key pins---
key_pins = (
board.KEY1, board.KEY2, board.KEY3,
board.KEY4, board.KEY5, board.KEY6,
board.KEY7, board.KEY8, board.KEY9,
board.KEY10, board.KEY11, board.KEY12
)
# ---Create keypad object---
keys = keypad.Keys(key_pins, value_when_pressed=False, pull=True)
# ---Official Launchpad colors---
LP_COLORS = (
0x000000, 0x101010, 0x202020, 0x3f3f3f, 0x3f0f0f, 0x3f0000, 0x200000, 0x100000,
0x3f2e1a, 0x3f0f00, 0x200800, 0x100400, 0x3f2b0b, 0x3f3f00, 0x202000, 0x101000,
0x213f0c, 0x143f00, 0x0a2000, 0x051000, 0x123f12, 0x003f00, 0x002000, 0x001000,
0x123f17, 0x003f06, 0x002003, 0x001001, 0x123f16, 0x003f15, 0x00200b, 0x001006,
0x123f2d, 0x003f25, 0x002012, 0x001009, 0x12303f, 0x00293f, 0x001520, 0x000b10,
0x12213f, 0x00153f, 0x000b20, 0x000610, 0x0b093f, 0x00003f, 0x000020, 0x000010,
0x1a0d3e, 0x0b003f, 0x060020, 0x030010, 0x3f0f3f, 0x3f003f, 0x200020, 0x100010,
0x3f101b, 0x3f0014, 0x20000a, 0x100005, 0x3f0300, 0x250d00, 0x1d1400, 0x080d01,
0x000e00, 0x001206, 0x00051b, 0x00003f, 0x001113, 0x040032, 0x1f1f1f, 0x070707,
0x3f0000, 0x2e3f0b, 0x2b3a01, 0x183f02, 0x032200, 0x003f17, 0x00293f, 0x000a3f,
0x06003f, 0x16003f, 0x2b061e, 0x0a0400, 0x3f0c00, 0x213701, 0x1c3f05, 0x003f00,
0x0e3f09, 0x153f1b, 0x0d3f32, 0x16223f, 0x0c1430, 0x1a1439, 0x34073f, 0x3f0016,
0x3f1100, 0x2d2900, 0x233f00, 0x201601, 0x0e0a00, 0x001203, 0x031308, 0x05050a,
0x050716, 0x190e06, 0x200000, 0x36100a, 0x351204, 0x3f2f09, 0x27370b, 0x192c03,
0x05050b, 0x36341a, 0x1f3a22, 0x26253f, 0x23193f, 0x0f0f0f, 0x1c1c1c, 0x373f3f,
0x270000, 0x0d0000, 0x063300, 0x011000, 0x2d2b00, 0x0f0c00, 0x2c1400, 0x120500,
)
LP_PADS = {
81: 0, 82: 1, 83: 2,
71: 3, 72: 4, 73: 5,
61: 6, 62: 7, 63: 8,
51: 9, 52: 10, 53: 11
}
LIVE_NOTES = [81, 82, 83, 71, 72, 73, 61, 62, 63, 51, 52, 53]
CC_OFFSET = 20
modifier = False # use to add encoder switch modifier to keys for clip mute
MODIFIER_NOTES = [41, 42, 43, 41, 42, 43, 41, 42, 43, 41, 42, 43] # blank row in Live
# ---Encoder setup---
encoder = rotaryio.IncrementalEncoder(board.ENCODER_B, board.ENCODER_A)
sw = DigitalInOut(board.ENCODER_SWITCH)
sw.switch_to_input(pull=Pull.UP)
switch = Debouncer(sw)
last_position = 0 # encoder position state
# ---NeoPixel setup---
BRIGHT = 0.125
DIM = 0.0625
pixels = neopixel.NeoPixel(board.NEOPIXEL, 12, brightness=BRIGHT, auto_write=False)
# ---Display setup---
display = board.DISPLAY
screen = displayio.Group(max_size=12)
display.show(screen)
WIDTH = 128
HEIGHT = 64
FONT = terminalio.FONT
# Draw a title label
title = TITLE_TEXT
title_area = label.Label(FONT, text=title, color=0xFFFFFF,x=6, y=3)
screen.append(title_area)
# --- create display strings and positions
x1 = 5
x2 = 35
x3 = 65
y1 = 17
y2 = 27
y3 = 37
y4 = 47
y5 = 57
# ---Push knob text setup
push_text_area = label.Label(FONT, text="[o]", color=0xffffff, x=WIDTH-22, y=y2)
screen.append(push_text_area)
# ---CC knob text setup
fader_text_area = label.Label(FONT, text=FADER_TEXT, color=0xffffff, x=WIDTH - 42, y=y4)
screen.append(fader_text_area)
# --- cc value display
cc_val_text = str(CC_OFFSET)
cc_val_text_area = label.Label(FONT, text=cc_val_text, color=0xffffff, x=WIDTH - 20, y=y5)
screen.append(cc_val_text_area)
label_data = (
# text, x, y
(TRACK_NAMES[0], x1, y1), (TRACK_NAMES[1], x2, y1), (TRACK_NAMES[2], x3, y1),
(".", x1, y2), (".", x2, y2), (".", x3, y2),
(".", x1, y3), (".", x2, y3), (".", x3, y3),
(".", x1, y4), (".", x2, y4), (".", x3, y4),
(".", x1, y5), (".", x2, y5), (".", x3, y5)
)
labels = []
for data in label_data:
text, x, y = data
label_area = label.Label(FONT, text=text, color=0xffffff)
group = displayio.Group(max_size=4, x=x, y=y)
group.append(label_area)
screen.append(group)
labels.append(label_area) # these can be individually addressed later
num = 1
while True:
msg_in = midi.receive()
if isinstance(msg_in, NoteOn) and msg_in.velocity != 0:
print(
"received NoteOn",
"from channel",
msg_in.channel + 1,
"MIDI note",
msg_in.note,
"velocity",
msg_in.velocity,
"\n"
)
# send neopixel lightup code to key, text to display
if msg_in.note < 84 and msg_in.note > 50:
pixels[LP_PADS[msg_in.note]] = LP_COLORS[msg_in.velocity]
pixels.show()
if msg_in.velocity == 21: # active pad is indicated by Live as vel 21
labels[LP_PADS[msg_in.note]+3].text = "o"
else:
labels[LP_PADS[msg_in.note]+3].text = "."
elif (
isinstance(msg_in, NoteOff)
or isinstance(msg_in, NoteOn)
and msg_in.velocity == 0
):
print(
"received NoteOff",
"from channel",
msg_in.channel + 1,
"MIDI note",
msg_in.note,
"velocity",
msg_in.velocity,
"\n"
)
elif isinstance(msg_in, ControlChange):
print(
"received CC",
"from channel",
msg_in.channel + 1,
"controller",
msg_in.control,
"value",
msg_in.value,
"\n"
)
elif isinstance(msg_in, MIDIUnknownEvent):
# Message are only known if they are imported
print("Unknown MIDI event status ", msg_in.status)
elif msg_in is not None:
midi.send(msg_in)
event = keys.events.get() # check for keypad events
if not event: # Event is None; no keypad event happened, do other stuff
position = encoder.position # store encoder position state
cc_position = constrain((position + CC_OFFSET), 0, 127) # lock to cc range
if last_position is None or position != last_position:
if position < last_position:
midi.send(ControlChange(LIVE_CC_NUMBER, cc_position))
print("CC", cc_position)
cc_val_text_area.text = str(cc_position)
elif position > last_position:
midi.send(ControlChange(LIVE_CC_NUMBER, cc_position))
print("CC", cc_position)
cc_val_text_area.text = str(cc_position)
last_position = position
switch.update() # check the encoder switch w debouncer
if switch.fell:
print("Mod")
push_text_area.text = "[.]"
modifier = True
pixels.brightness = DIM
pixels.show()
if switch.rose:
modifier = False
push_text_area.text = "[o]"
pixels.brightness = BRIGHT
pixels.show()
continue
num = event.key_number
if event.pressed and not modifier:
midi.send(NoteOn(LIVE_NOTES[num], 127))
print("\nsent note", LIVE_NOTES[num], "\n")
if event.pressed and modifier:
midi.send(NoteOn(MODIFIER_NOTES[num], 127))
if event.released and not modifier:
midi.send(NoteOff(LIVE_NOTES[num], 0))
if event.released and modifier:
midi.send(NoteOff(MODIFIER_NOTES[num], 0))
pixels.show()