Skip to content

Instantly share code, notes, and snippets.

@cfstras
Created June 3, 2018 21:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cfstras/34c36a30813e397bd744b968977bebc8 to your computer and use it in GitHub Desktop.
Save cfstras/34c36a30813e397bd744b968977bebc8 to your computer and use it in GitHub Desktop.
Launchpad midi example with particles and Leap-motion integration
import threading
import time
import mido
callbacks = []
particles = []
particles_lock = threading.Lock()
def clamp(v, min, max):
if v > max: return max
if v < min: return min
return v
def xy_to_note(x, y):
x = int(clamp(x, 0, 7))
y = int(clamp(y, 0, 7))
return y * 16 + x
def note_to_xy(note):
if type(note) is mido.Message:
note = note.note
return note % 8, note / 16
def start_midi():
launchpad_names = [n for n in mido.get_input_names() if "Launchpad" in n]
if not launchpad_names:
print("Error: no launchpad")
return
midi_in = threading.Thread(target=midi_in_thread, args=(launchpad_names[0],))
midi_out = threading.Thread(target=midi_out_thread, args=(launchpad_names[0],))
midi_in.daemon = True
midi_in.start()
midi_out.daemon = True
midi_out.start()
def midi_out_thread(name):
global particles
output = mido.open_output(name)
while True:
add_particles = []
for c in callbacks:
add_particles.extend(c())
particles_lock.acquire()
particles.extend(add_particles)
for p in particles:
p.render(output)
particles = [p for p in particles if p.valid()]
particles_lock.release()
time.sleep(1.0/30)
def midi_in_thread(name):
input = mido.open_input(name)
while True:
msg = input.receive()
if "note" not in msg.type: continue
x, y = note_to_xy(msg)
if msg.velocity > 0:
particles_lock.acquire()
particles.append(Particle(x, y, 1, 0))
particles.append(Particle(x, y, 0, 1))
particles.append(Particle(x, y, -1, 0))
particles.append(Particle(x, y, 0, -1))
particles_lock.release()
class Particle(object):
def __init__(self, x, y, vx=0, vy=0, max_age=float('inf')):
self.x = x
self.y = y
self.vx = vx
self.vy = vy
self.max_age = max_age
self.age = 0
self.old_pos = None
def tick(self):
if not self.in_screen():
return
old_pos = (self.x, self.y)
self.x += self.vx
self.y += self.vy
self.age += 1
if int(old_pos[0]) != int(self.x) or \
int(old_pos[1]) != int(self.y) or \
not self.in_screen():
self.old_pos = old_pos
def in_screen(self):
return self.x >= 0 and self.x < 8 \
and self.y >= 0 and self.y < 8 \
and self.age <= self.max_age
def valid(self):
return self.in_screen() or self.old_pos
def color(self):
v = 1 - (self.age / 8.0)
v = clamp(v, 0, 1)
return int(v * 127)
def render(self, output):
if self.old_pos:
note = xy_to_note(self.old_pos[0], self.old_pos[1])
msg = mido.Message('note_on', note=note,
velocity=0)
output.send(msg)
self.old_pos = None
if self.in_screen():
msg = mido.Message('note_on', note=xy_to_note(self.x, self.y),
velocity=self.color())
output.send(msg)
self.tick()
#!/usr/bin/env python2
from __future__ import print_function
import sys
import launchpad
import lib.Leap as leap
def main(args):
global controller
controller = leap.Controller()
launchpad.start_midi()
launchpad.callbacks.append(particle_leap)
print("Press enter to exit...")
try:
sys.stdin.readline()
except KeyboardInterrupt:
pass
finally:
#controller.remove_listener(listener)
pass
def particle_leap():
min_x, min_y, max_x, max_y = -150, -70, 150, 70
for hand in controller.frame().hands:
pos = hand.palm_position
x, y = pos.x, pos.z
x, y = (x - min_x) / (max_x - min_x), (y - min_y) / (max_y - min_y)
x, y = x * 8, y * 8
#yield launchpad.Particle(x, y,
# random.randint(-1, 1), random.randint(-1, 1),
# max_age=10)
yield launchpad.Particle(x, y, max_age=20)
if __name__ == "__main__":
sys.exit(main(sys.argv))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment