Created
June 3, 2018 21:43
-
-
Save cfstras/34c36a30813e397bd744b968977bebc8 to your computer and use it in GitHub Desktop.
Launchpad midi example with particles and Leap-motion integration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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