Dup Ver Goto 📝

Nocturn To Hedgehog

To
241 lines, 781 words, 7332 chars Page 'NocturnHedgehog' does not exist.

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])