view bcf2000.py @ 2405:69ca2b2fc133

overcomplicated attempt at persisting the pane layout in the rdf graph this was hard because we have to somehow wait for the graph to load before config'ing the panes
author drewp@bigasterisk.com
date Fri, 17 May 2024 16:58:26 -0700
parents 02eb0e99bba5
children
line wrap: on
line source

#!/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()