Files @ 9b6f4b3c329c
Branch filter:

Location: light9/bcf2000.py

drewp@bigasterisk.com
correct some output loop rates; clean up
#!/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 = b""
        loop = LoopingCall(self.poll)
        loop.start(.01)

    def poll(self):
        try:
            newData = self.dev.read(3)
        except (IOError, AttributeError):
            return
        if newData is None:
            return
        self.packet += newData
        if len(self.packet) == 3:
            p = self.packet
            self.packet = b""
            self.packetReceived(p)

    def packetReceived(self, packet):
        b0, which, value = 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, "rb+", buffering=0)
        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(bytes([0xb0, which[0], int(value)]))

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


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