2025apr7
todo: x design enclosure, finish code, make vids
x fluidpatcher: add flag for internal vs. external router to Synth, MidiPlayer
x sequencer: multi-track, loop counts, patterns
Sequencer --> Sequence
x  add multi-track, mod listing format
x  patterns, sequence, (mute/modify notes done by user)
x  manages event list + times, callback
x  Synth methods: schedule_event(id, time, event), clear_events(id, type), schedule_callback(id, time)  
Arpeggio - add patterns
MidiLoop class: records/repeats midi messages
sequences:
  song1:
    events1: |
      note:5:C4:100  note:6:C2:100  note:10:32:100
      note:5:F4:100  +              note:10:32:100
      note:5:G4:100  note:6:C2:100  note:10:32:100
      note:5:E4:100  +              note:10:32:100
    events2: [[note:5:C4:100, note:5:F4:100, note:5:G4:100, note:5:E4:100],
             [note:5:C4:100, note:5:F4:100, note:5:G4:100, note:5:E4:100]]
    events3: note:5:C4:100,note:5:F4:100,note:5:G4:100,note:5:E4:100

2025mar21
log taper demo for docs:
https://www.desmos.com/calculator/ci9saidi6m

software tasks:
  no need for router rule tags - silencing channels can be done with bank change CC
  add Bank objects
    handles merging of patch, bank zones
    simplifies writing new elements into bank (router rules, fluidsettings)
  get rid of solo_soundfont
  SoundFont objects in pfluidsynth.py contain preset names, banks, numbers
  add "layer" creation/control to squishbox.py
    set preset on any channel
    create/modify note routing - key range, key shift
  modify bankfiles.RouterRule class
    channelspec tuples are all 1-1 routing
    expose RouterRule class to user
  logarithmic value routing
    type: cc, num: 15=74, val: 0-127=0-127, log: 100 # log taper equation with b=100
  routing to MSB/LSB pairs
    type: cc, num: 16=5, val: 0-127=0-500, lsb: 37 # routes MSB of val to 5, LSB to 37
  routing to incremental values
    type: cc=prog, num: 17, val: 3-5*0+2, inc: 1 # range 3-5, inital value 2, increment by 1
  names: item in bank files creates aliases for values (only in router rules and messages?)
    names:
      reverb: 91
      slider5: 19
    router_rules:
      - {type: cc, chan: 1=4, num: slider5=reverb}
  bindings for fluid_synth_add_default_mod, fluid_synth_remove_default_mod
  router rules can have multiple functions i.e. CC and lcdwrite
  a better menu creation system for squishbox - event loop/callback thingy?
r = RouterRule(**{type: 'cpress=pbend', chan: '1=2', val: '0-127=0-8192', log: 100}) -- looks bad
r = RouterRule("type: 'cpress=pbend', chan: '1=2', val: '0-127=0-8192', log: 100") -- even worse
r = RouterRule(type='cpress=pbend', chan='1=2', val='0-127=0-8192', log=100) -- correct form

m = MidiMessage(**{type: 'note', chan: 5, num: 'F#5', val: 95}) -- worst way
m = MidiMessage(type='note', chan=5, num='F#5', val=95)
m = MidiMessage('note', 5, 'F#5', 95) -- fine
m = MidiMessage(text='note:5:F#5:95') -- saves no space

m = MidiMessage('pbend', 5, val=3123)
m = MidiMessage(text='pbend:5:3123') -- saves no space

m = MidiMessage('prog', 5, val=34)
m = MidiMessage('sysex', val=[5,4,5,32,6,2,54,1])

router_rules --> rules
midiplayer: chan: --> shift
fix bankfiles.midimessage
fix router integration
  should router.py use rtmidi or be a custom router for fluidsynth?
  fluidpatcher should only do fluidsynth things - if it has its own midi port it starts to look like a general router
  squishbox probably _will_ use rtmidi for some routing, so fluidpatcher should not
  maybe this means fluidpatcher should not have a midi callback? what if squishbox can filter midi?
make a "proper" __init__ file?