tags: midi hedgehog nocturn title: Nocturn To Hedgehog See [Hedgehog001](/hedgehog/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. ```py 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]) ```