# HG changeset patch # User Drew Perttula # Date 2007-06-10 07:41:48 # Node ID c7478a778992261169bf5f99904866d895851347 # Parent 768d0d12c3a4bfcc9d9167b2cf230ca6c56e7f8b junky first pass at bcf2000 for keyboardcomposer diff --git a/bcf2000.py b/bcf2000.py new file mode 100644 --- /dev/null +++ b/bcf2000.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +from __future__ import division +import math +import twisted.internet.fdesc +from twisted.internet import reactor +from twisted.internet.task import LoopingCall + +class BCF2000(object): + + control = {81 : "slider1", 82 : "slider2", 83 : "slider3", 84 : "slider4", + 85 : "slider5", 86 : "slider6", 87 : "slider7", 88 : "slider8", + + 1 : "knob1", 2 : "knob2", 3 : "knob3", 4 : "knob4", + 5 : "knob5", 6 : "knob6", 7 : "knob7", 8 : "knob8", + + 33 : "button-knob1", 34 : "button-knob2", + 35 : "button-knob3", 36 : "button-knob4", + 37 : "button-knob5", 38 : "button-knob6", + 39 : "button-knob7", 40 : "button-knob8", + + 65 : "button-upper1", 66 : "button-upper2", + 67 : "button-upper3", 68 : "button-upper4", + 69 : "button-upper5", 70 : "button-upper6", + 71 : "button-upper7", 72 : "button-upper8", + 73 : "button-lower1", 74 : "button-lower2", + 75 : "button-lower3", 76 : "button-lower4", + 77 : "button-lower5", 78 : "button-lower6", + 79 : "button-lower7", 80 : "button-lower8", + 89 : "button-corner1", 90 : "button-corner2", + 91 : "button-corner3", 92 : "button-corner4", + } + + def __init__(self, dev="/dev/snd/midiC1D0"): + self.devPath = dev + self.dev = None + self.reopen() + self.lastValue = {} # control name : value + self.packet = "" + loop = LoopingCall(self.poll) + loop.start(.02) + + def poll(self): + try: + bytes = self.dev.read(3) + except IOError, e: + return + if len(bytes) == 0: + print "midi stall, reopen slider device" + self.reopen() + return + self.packet += bytes + if len(self.packet) == 3: + p = self.packet + self.packet = "" + self.packetReceived(p) + + return + + while 1: + packet = self.dev.read(3) + while len(packet) < 3: + if len(packet) == 0: + self.reopen() + packet += self.dev.read(3 - len(packet)) + + def packetReceived(self, packet): + b0, which, value = [ord(b) for b in packet] + if b0 != 0xb0: + return + if which in self.control: + name = self.control[which] + if name.startswith("button-"): + value = value > 0 + self.lastValue[name] = value + self.valueIn(name, value) + else: + print "unknown control %s to %s" % (which, value) + + def reopen(self): + if self.dev is not None: + try: + self.dev.close() + except IOError: + pass + + self.dev = open(self.devPath, "r+") + twisted.internet.fdesc.setNonBlocking(self.dev) + + def valueIn(self, name, value): + """override this with your handler for when events come in + from the hardware""" + print "slider %s to %s" % (name, value) + if name == 'slider1': + for x in range(2,8+1): + v2 = int(64 + 64 * math.sin(x / 3 + value / 10)) + self.valueOut('slider%d' % x, v2) + for x in range(1,8+1): + self.valueOut('button-upper%s' % x, value > x*15) + self.valueOut('button-lower%s' % x, value > (x*15+7)) + + def valueOut(self, name, value): + """call this to send an event to the hardware""" + value = int(value) + if self.lastValue.get(name) == value: + return + self.lastValue[name] = value + which = [k for k,v in self.control.items() if v == name] + assert len(which) == 1, "unknown control name %r" % name + if isinstance(value, bool): + value = value * 127 + self.dev.write(chr(0xb0) + chr(which[0]) + chr(int(value))) + + +if __name__ == '__main__': + b = BCF2000() + reactor.run() diff --git a/bin/keyboardcomposer b/bin/keyboardcomposer --- a/bin/keyboardcomposer +++ b/bin/keyboardcomposer @@ -16,6 +16,7 @@ from light9.Submaster import Submasters, from light9.subclient import SubClient from light9 import dmxclient, showconfig, networking from light9.uihelpers import toplevelat, bindkeys +from bcf2000 import BCF2000 nudge_keys = { 'up' : list('qwertyuiop'), @@ -106,6 +107,9 @@ class KeyboardComposer(Frame, SubClient) self.sub_name = Entry(self.buttonframe, bg='black', fg='white') self.sub_name.pack(side=LEFT) self.stop_frequent_update_time = 0 + + self.sliders = Sliders(self.hw_slider_moved) + def make_key_hints(self): keyhintrow = Frame(self) @@ -171,6 +175,11 @@ class KeyboardComposer(Frame, SubClient) subtk.scale.fade(0) else: subtk.scale.decrease() + + def hw_slider_moved(self, col, value): + subtk = self.slider_table[(self.current_row, col)] + subtk.scale.set(value) + def draw_sliders(self): self.tk_focusFollowsMouse() @@ -187,9 +196,12 @@ class KeyboardComposer(Frame, SubClient) col += 1 col %= 10 - def slider_changed(x, y, z, subtk=subtk): + def slider_changed(x, y, z, subtk=subtk, col=col, sub=sub): subtk.scale.draw_indicator_colors() self.send_levels() + v = 127 * self.get_levels()[sub.name] + self.sliders.valueOut("slider%s" % (col), v) + subtk.slider_var.trace('w', slider_changed) def make_row(self): @@ -265,6 +277,15 @@ class LevelServer(xmlrpc.XMLRPC): ret=str(e) return ret +class Sliders(BCF2000): + def __init__(self, cb): + BCF2000.__init__(self) + self.cb = cb + def valueIn(self, name, value): + print "in", name, value + if name.startswith("slider"): + self.cb(int(name[6:]) - 1, value / 127) + if __name__ == "__main__": parser = OptionParser() parser.add_option('--nonpersistent', action="store_true",