See Hedgehog001 for the core code.
The idea of the Delegate system is that I just have to make
a class inheriting from the delegate base, and override e.g.
rot(i,dx) with the translating from MIDI CC's to methods
like rot(i,dx) and but(i,x) done in the delegate base.
This needs to happen in the delegate base as the MIDI CC's
are different for difference devices. So each device has a
corresponding delegate base we inherit from.
from icecream import ic
from hh_base import MidiDeviceBase, DelegateBase
from hh_util import decode_rel
# The Device classes managing talking MIDI to the devices,
# and forwarding messages to/from the delegate.
# One idea common to Hedgehog is to keep stuff as raw
# events for a while, so the delegate is responsible for
# turning CC numbers into Hedgehog events.
class NocturnDeviceBase(MidiDeviceBase):
def __init__(self,*xs,**kw):
print(124,f"{xs=} {kw=}")
super().__init__(*xs,**kw)
# Functionality is largely in MidiDeviceBase now
# The DeviceSwitch class is there to forward to a selected
# delegate, the idea being that the delegate can be switched
# at runtime, though I haven't yet followed up on this idea.
class NocturnDeviceSwitch(NocturnDeviceBase):
def __init__(self,inport,outport,name="Nocturn"):
super().__init__(inport,outport,name=name)
class NocturnDelegateBase(DelegateBase):
def __init__(self,hub,device,name="Nocturn"):
super().__init__(hub,device,name)
self.rot_base = 0x40
self.num_rots = 8
self.num_buts = 16
self.but_base = 0x70
self.rrot_base = 0x4A
self.rrot_led_base = 0x50
self.touch_base = 0x60
self.touch_rrot = 0x52
def recv_cc(self,n,v):
if n in [ 72, 73, 83 ]:
return
print(f"Noct cc {n=}=0x{n=:x} {v=}=0x{v=:x}")
if n == self.rrot_base:
self.rrot(v)
elif n >= self.rot_base and n < self.rot_base + self.num_rots:
self.rot(n-self.rot_base,v)
elif n >= self.but_base and n < self.but_base + self.num_buts:
self.but(n-self.but_base,v)
elif n >= self.touch_base and n < self.touch_base + self.num_rots:
self.touch(n-self.touch_base,v)
elif n == self.touch_rrot:
self.touch(8,v)
def recv_on(self,p,v):
print(f"Noct on {p=} {v=}")
def recv_off(self,p,v):
print(f"Noct off {p=} {v=}")
def recv_osc(self,addr,params):
print(f"Noct osc {addr=} {params=}")
def set_all_buts(self,v):
for i in range(self.num_buts):
self.set_but(i,v)
def set_all_rots(self,v):
for i in range(self.num_rots):
self.set_rot(i,v)
def set_but(self,n,v):
print(f"set_but {n=} {v=}")
if n < 0 or n >= self.num_buts:
return
self.device.send_cc(self.but_base+n,v)
def set_rot(self,n,v):
if n < 0 or n >= self.num_rots:
return
self.device.send_cc(self.rot_base+n,v)
def set_rrot(self,v):
self.device.send_cc(self.rrot_led_base,v)
# Example Delegates
class NocturnDelegate_16Chan(NocturnDelegateBase):
"Simple nocturn handler class. Rough knob selects channel."
# TODO: Move much of this (like nrots) into NocturnBase
def __init__(self,hub,device,chan_base=0,name="Nocturn x16"):
super().__init__(hub,device,name=name)
self.rrot_gran = 2
self.rot_gran = 2
self.num_chs = 16
self.ch = self.num_chs - 1
self.chan_base = chan_base
self.rrot_val = 0
self.rot_vals = [0]*self.num_rots
self.touch_state = [0]*9
# For 'gearing' the knobs. Replace with a suitable class (GearedKnob)
def init(self):
self.activate()
def touch(self,n,v):
self.touch_state[n] = 1 if v > 0 else 0
def rrot(self,v):
print(f"rrot {v=}")
v = decode_rel(v)
old = self.rrot_val
oldx = old // self.rrot_gran
self.rrot_val = self.rrot_val + v
newx = self.rrot_val // self.rrot_gran
dx = newx - oldx
if dx != 0:
self.hub.send_dx(self.chan_base+self.ch,self.num_rots,dx)
def set_ch(self,nch):
print(f"setch {nch=}")
och = self.ch
self.set_but(och,0)
self.set_but(nch,1)
self.ch = nch
self.hub.send_debug(f"{self.name} channel now {self.ch}")
def deactivate(self):
self.set_all_buts(0)
self.set_all_rots(0)
def activate(self):
self.set_all_buts(0)
self.set_all_rots(0)
self.set_rrot(0)
ch = self.ch
if ch < 0:
ch = 7
self.set_ch(ch)
def __del__(self):
# switch bank light off
print(f"Deac {self.name}")
self.deactivate()
super().__del__()
def rot(self,i,v):
if v >= 0x40:
v -= 0x80 # to signed int
v0 = self.rot_vals[i] // self.rot_gran
self.rot_vals[i] = (self.rot_vals[i] + v)
v1 = self.rot_vals[i] // self.rot_gran
dv = v1 - v0
print(f"rot {self.name=} {i=} {v=} {self.rot_vals[i]=} {dv=}")
if dv != 0:
self.hub.send_dx(self.chan_base+self.ch,i,dv)
def but(self,i,x):
print(f"Nocturn but {i=} {x=}")
if self.touch_state[8] > 0 or self.touch_state[0] > 0:
self.set_ch(i)
return
if x > 0:
self.hub.send_but(self.chan_base+self.ch,i,1)
else:
self.hub.send_but(self.chan_base+self.ch,i,0)
class NocturnDelegate_LastBut(NocturnDelegate_16Chan):
"""As Nocturn, but the last pressed button lights up and others switch off.
Intended for when the buttons select instrument or something."""
def __init__(self,hub,device,chan_base=0,name="NocturnLastBut"):
super().__init__(hub=hub,device=device,chan_base=chan_base,name=name)
self.last_but = -1
self.ch = 0
def init(self):
self.activate()
self.set_last_but(4)
def activate(self):
self.set_all_buts(0)
self.set_all_rots(0)
def set_ch(self,n):
return
def rrot(self,v):
if v >= 0x40:
v -= 0x80 # to signed int
v0 = self.rrot_val // self.rrot_gran
self.rrot_val = (self.rrot_val + v)
v1 = self.rrot_val // self.rrot_gran
dv = v1 - v0
if dv != 0:
self.hub.send_dx(self.ch,self.num_rots,dv)
def set_last_but(self,n):
# switch off all buttons except the last pressed
if self.last_but >= 0 and n != self.last_but:
self.set_but(self.last_but,0)
self.last_but = n % 16
self.set_but(self.last_but,127)
def but(self,i,x):
print(f"NocturnLastBut but {i=} {x=}")
if x > 0:
self.set_last_but(i)
super().but(i,x)
class NocturnDelegate_ToggleBut(NocturnDelegate_16Chan):
"""As Nocturn, but the last pressed button lights up and others switch off.
Intended for when the buttons select instrument or something."""
def __init__(self,hub,device,chan_base=0,name="NocturnLastBut"):
super().__init__(hub=hub,device=device,chan_base=chan_base,name=name)
self.but_states = [0]*self.num_buts
self.ch = 0
def init(self):
self.activate()
def activate(self):
self.set_all_buts(0)
self.set_all_rots(0)
self.set_rot(7,127)
def set_ch(self,n):
return
def rrot(self,v):
if v >= 0x40:
v -= 0x80 # to signed int
v0 = self.rrot_val // self.rrot_gran
self.rrot_val = (self.rrot_val + v)
v1 = self.rrot_val // self.rrot_gran
dv = v1 - v0
if dv != 0:
self.hub.send_dx(self.ch,self.num_rots,dv)
def but(self,i,x):
if x > 0:
self.but_states[i] = 1 - self.but_states[i]
self.hub.send_but(self.chan_base+self.ch,i,self.but_states[i])
self.set_but(i,self.but_states[i])