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()