Files @ 9b5e313d6dfc
Branch filter:

Location: light9/bcf2000.py - annotation

drewp@bigasterisk.com
checkpoint show data
Ignore-this: 53672a71784dd435d2f70de16f8c1565
c7478a778992
f001d689b3e2
c7478a778992
c7478a778992
c7478a778992
c7478a778992
f001d689b3e2
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
b2858c2d4a4d
ff9c72cfb023
b2858c2d4a4d
b2858c2d4a4d
c7478a778992
c7478a778992
f001d689b3e2
c7478a778992
c7478a778992
c7478a778992
bd8a89743226
c7478a778992
c7478a778992
c7478a778992
c7478a778992
551f06733559
c7478a778992
c7478a778992
f001d689b3e2
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
f001d689b3e2
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
48af0ea86ff4
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
f001d689b3e2
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
c7478a778992
551f06733559
551f06733559
551f06733559
c7478a778992
c7478a778992
c7478a778992
c7478a778992
f001d689b3e2
c7478a778992
68e1655eaa2c
c7478a778992
fc87327e29c4
c7478a778992
551f06733559
551f06733559
551f06733559
551f06733559
c7478a778992
c7478a778992
c7478a778992
c7478a778992
#!/usr/bin/python

import math
import twisted.internet.fdesc
from twisted.internet import reactor
from twisted.internet.task import LoopingCall
from typing import Dict

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/midiC2D0"):
        """device was usually /dev/snd/midiC1D0 but then it showed up
        once as C0D0. It should be autodetected"""
        self.devPath = dev
        self.dev = None
        self.lastValue: Dict[str, int] = {} # control name : value
        self.reopen()
        self.packet = ""
        loop = LoopingCall(self.poll)
        loop.start(.01)

    def poll(self):
        try:
            bytes = self.dev.read(3)
        except (IOError, AttributeError):
            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)

    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.lastValue.clear()
        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"""
        if self.dev is None:
            return

        value = int(value)
        if self.lastValue.get(name) == value:
            return
        self.lastValue[name] = value
        which = [k for k,v in list(self.control.items()) if v == name]
        assert len(which) == 1, "unknown control name %r" % name
        if name.startswith('button-'):
            value = value * 127
        #print "bcf: write %s %s" % (name, value)
        self.dev.write(chr(0xb0) + chr(which[0]) + chr(int(value)))

    def close(self):
        self.dev.close()
        self.dev = None

if __name__ == '__main__':
    b = BCF2000()
    reactor.run()