first commit Live launcher code
This commit is contained in:
parent
c6cdb94ecb
commit
fa4375a281
1 changed files with 252 additions and 0 deletions
252
Macropad_Ableton/code.py
Normal file
252
Macropad_Ableton/code.py
Normal 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()
|
||||
Loading…
Reference in a new issue