I converted a midi device into a macro machine. I did this mostly to manage my video calls.
As a professional manager in a indefinite-work-from-home situation, making and managing video calls has become a large part of my job.
The rest of this post describes what it does, why I picked this device, some of the choices I made, and the code that makes it possible.
The obvious answer is "I had it" but I had it because:
Some things that might change if this were to become A Thing
It's a few hundred lines of python and a library dedicated to converting python to midi for these devices.
#!/usr/bin/env python
import sys
import launchpad_py as launchpad
import random
import subprocess
from pygame import time
import pyautogui
from pprint import pprint
import datetime
pyautogui.PAUSE = 0
pyautogui.FAILSAFE = False
KEYS = {
"key0": {
"title": "Music Please",
"kind": "function_call",
"func": "music_please",
"kargs": {},
},
"key82": {
"title": "Output: Dock Headphones",
"kind": "shell",
"cmd": '''pacmd set-default-sink alsa_output.usb-Lenovo_ThinkPad_USB-C_Dock_Audio_000000000000-00.analog-stereo''',
"colors": {
"on": {
"cmd": '''pacmd dump | grep "set-default-sink alsa_output.usb-Lenovo_ThinkPad_USB-C_Dock_Audio_000000000000-00.analog-stereo" && echo "0,2" '''
}
}
},
"key83": {
"title": "Output: Speakers",
"kind": "shell",
"cmd": '''pacmd set-default-sink alsa_output.usb-Dell_Dell_AC511_USB_SoundBar-00.analog-stereo''',
"colors": {
"on": {
"cmd": '''pacmd dump | grep "set-default-sink alsa_output.usb-Dell_Dell_AC511_USB_SoundBar-00.analog-stereo" && echo "0,2" '''
}
}
},
"key84": {
"title": "Output: Yeti Headphones",
"kind": "shell",
"cmd": '''pacmd set-default-sink alsa_output.usb-Blue_Microphones_Yeti_Nano_2028SG005AS8_888-000154040606-00.iec958-stereo''',
"colors": {
"on": {
"cmd": '''pacmd dump | grep "set-default-sink alsa_output.usb-Blue_Microphones_Yeti_Nano_2028SG005AS8_888-000154040606-00.iec958-stereo" && echo "0,2" '''
}
}
},
"key85": {
"title": "Input: Yeti Mic",
"kind": "shell",
"cmd": '''pacmd set-default-source alsa_input.usb-Blue_Microphones_Yeti_Nano_2028SG005AS8_888-000154040606-00.analog-stereo''',
"colors": {
"on": {
"cmd": '''pacmd dump | grep "set-default-source alsa_input.usb-Blue_Microphones_Yeti_Nano_2028SG005AS8_888-000154040606-00.analog-stereo" && echo "0,2" '''
}
}
},
"key86": {
"title": "Input: Cam Mic",
"kind": "shell",
"cmd": '''pacmd set-default-source alsa_input.usb-046d_HD_Pro_Webcam_C920_03D377DF-02.analog-stereo''',
"colors": {
"on": {
"cmd": '''pacmd dump | grep "set-default-source alsa_input.usb-046d_HD_Pro_Webcam_C920_03D377DF-02.analog-stereo" && echo "0,2" '''
}
}
},
"key99": {
"title": "Launch Zoom",
"kind": "shell",
"cmd": '''xdg-open zoommtg://my-meeting-dontcopythis'''
},
"key100": {
"title": "Copy Zoom Link",
"kind": "shell",
"cmd": '''echo 'https://my-meeting-dontcopythis' | xclip -selection clipboard'''
},
"key101": {
"title": "Kill Zoom",
"kind": "shell",
"cmd": '''killall zoom'''
},
"key102": {
"title": "Unmute Me",
"kind": "shell",
"cmd": '''pactl set-source-volume @DEFAULT_SOURCE@ 80%''',
"colors": {
"on": {
"cmd": '''SOURCE=$(pacmd info | grep "Default source name: " | sed 's\Default source name: \\g'); pacmd dump | grep "set-source-volume $SOURCE" | grep 0x000 || echo 2,3'''
}
}
},
"key103": {
"title": "Volume Up",
"kind": "shell",
"cmd": '''pactl set-sink-volume @DEFAULT_SINK@ +10%'''
},
"key104": {
"title": "Volume Zero",
"kind": "shell",
"cmd": '''pactl set-sink-volume @DEFAULT_SINK@ 0%''',
"colors": {
"on": {
"cmd": '''SOURCE=$(pacmd info | grep "Default sink name: " | sed 's\Default sink name: \\g'); pacmd dump | grep "set-sink-volume $SOURCE" | grep 0x000 && echo 1,0'''
}
}
},
"key118": {
"title": "Mute Me",
"kind": "shell",
"cmd": '''pactl set-source-volume @DEFAULT_SOURCE@ 0%''',
"colors": {
"on": {
"cmd": '''SOURCE=$(pacmd info | grep "Default source name: " | sed 's\Default source name: \\g'); pacmd dump | grep "set-source-volume $SOURCE" | grep 0x000 && echo 3,0'''
}
}
#
},
"key119": {
"title": "Volume Down",
"kind": "shell",
"cmd": '''pactl set-sink-volume @DEFAULT_SINK@ -10%'''
}
}
def music_please():
subprocess.Popen("xdg-open https://music.youtube.com/", shell=True)
time.wait(300)
pyautogui.typewrite(['space'])
def main():
mode = None
lp = launchpad.Launchpad()
if lp.Open():
print("Launchpad Mk1/S/Mini")
mode = "Mk1"
if mode is None:
print("Did not find any Launchpads, meh...")
return
# scroll a string from right to left
#lp.LedCtrlString( "Starting", 0, 3, -1 )
lp.ButtonFlush()
lp.Reset()
reset_count = 15
iter_count = 0
while 1:
time.wait( 5 )
but = lp.ButtonStateRaw()
if but != []:
lp.LedCtrlRaw(8, random.randint(0,3),random.randint(0,3))
key, pressed = but
print('\t\t\t', "[event]", but )
if pressed:
lp.LedCtrlRaw( key, 1, 1)
if f'key{key}' in KEYS:
cmd = KEYS[f'key{key}']
print(f'Received: {cmd["title"]}')
if cmd["kind"] == "shell":
print(f'\tshell: \t\t{cmd["cmd"]}')
out = subprocess.Popen(cmd["cmd"], shell=True)
print('\t', out)
elif cmd["kind"] == "function_call":
eval(f'''{cmd["func"]}()''')
else:
print(f'\tUnknown kind: \t\t\{cmd["kind"]}')
else:
lp.LedCtrlRaw(key, 0, 0)
iter_count += 1
if iter_count == reset_count:
iter_count = 0
# Do Keys
for k, v in KEYS.items():
if 'colors' in v.keys():
key = int(k.replace('key', ''))
try:
process = subprocess.Popen(v['colors']['on']['cmd'], shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# Readlines, if last line has x,y then set them to r,g values, else set zeores
out, err = process.communicate()
lines = out.decode('utf-8').split('\n')
lines = list(filter(None, lines))
if lines:
try:
red, green = [int(x) for x in lines[-1].split(',', 1)]
#print(red, green)
lp.LedCtrlRaw(key, red, green)
continue
except ValueError:
pass
except subprocess.CalledProcessError:
pass
lp.LedCtrlRaw(key, 0, 0)
# now quit...
print("Quitting might raise a 'Bad Pointer' error (~almost~ nothing to worry about...:).\n\n")
lp.Reset() # turn all LEDs off
lp.Close() # close the Launchpad (will quit with an error due to a PyGame bug)
if __name__ == '__main__':
main()
31st January 2021
I won't ever give out your email address. I don't publish comments but if you'd like to write to me then you could use this form.
I'm Issac. I live in Oakland. I make things for fun and money. I use electronics and computers and software. I manage teams and projects top to bottom. I've worked as a consultant, software engineer, hardware designer, artist, technology director and team lead. I do occasional fabrication in wood and plastic and metal. I run a boutique interactive agency with my brother Kasey and a roving cast of experts at Kelly Creative Tech. I was the Director of Technology for Nonchalance during the The Latitude Society project. I was the Lead Web Developer and then Technical Marketing Engineer at Nebula, which made an OpenStack Appliance. I've been building things on the web and in person since leaving Ohio State University's Electrical and Computer engineering program in 2007. Lots of other really dorky things happened to me before that, like dropping out of high school to go to university, getting an Eagle Scout award, and getting 6th in a state-wide algebra competition. I have an affinity for hopscotch.