Skip to content

  • Projects
  • Groups
  • Snippets
  • Help
  • This project
    • Loading...
  • Sign in / Register
T
Tidal
  • Overview
    • Overview
    • Details
    • Activity
    • Cycle Analytics
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Charts
  • Issues 0
    • Issues 0
    • List
    • Board
    • Labels
    • Milestones
  • Merge Requests 0
    • Merge Requests 0
  • CI / CD
    • CI / CD
    • Pipelines
    • Jobs
    • Schedules
    • Charts
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Collapse sidebar
  • Activity
  • Graph
  • Charts
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
  • PLN
  • Tidal
  • Repository

Switch branch/tag
  • Tidal
  • tools
  • mock-lcxl.py
Find file
BlameHistoryPermalink
  • PLN (Algolia)'s avatar
    fix(lcxl): panic is a STATE, not a membership — and the six-step ramp was never wired in · 0b23f3d9
    Two colour bugs PLN found BY EYE, on the hardware, in the same glance. Both were
    cases of the code being confidently reasonable and visibly wrong.
    
    #76 — "why are [mutes] 1/2/3 resp red red green? these 3 buttons have same
    roles, why diff colours?"
    
    He was right and the cause was in two places at once. The panic feature is an
    OVERLOAD: hold LCXL buttons 73+74+91+92 together, the SC bridge edge-detects the
    chord and flips a persistent "^93" toggle, and gPanic gates on it. But those
    buttons have day jobs — 73 is gMute1, 74 is gMute2 — so:
    
      * control_colour tested `cc in PANIC_CHORD` BEFORE role, so 73/74 painted dim
        RED at rest while 75 (gMute3, identical job, not a chord member) painted by
        role. Three buttons, one job, two colours.
      * parse_track ALSO force-bound all four members to role "fx" (red hue) via
        setdefault, so even on a track binding gMute3 and not gMute1/2 the row read
        literally "R R G". Found by rendering mock-lcxl.py after fixing the first
        half and noticing the row still looked wrong.
    
    The panic identity does not exist at rest, so it must not be painted at rest.
    Now: `panic` is threaded in from the model (`values[93]`), the chord members
    paint by role like any other button when it is clear, and arming flashes red on
    all four as a GLOBAL OVERLAY applied last — over role, and over dark, because an
    unbound member must still flash. CC93 owns no LED of its own (it sits outside
    row F's 89-92), so those four buttons are the only place the armed state can
    live, and it is the highest-value LED in the rig: it answers "why is there no
    sound?". The watch loop rebuilds the whole frame on a ^93 flip rather than
    painting one index, since one event changes four LEDs.
    
    Verified by rendering the convention board: row F at rest is now
    `dimGRN dimGRN dimGRN dimAMB dimAMB dimAMB dimGRN dimAMB` — identical to row E,
    which is exactly what PLN reported seeing on row E ("G G G Y Y Y G Y"). Armed:
    `RED! RED! grn amb amb amb RED! RED!`.
    
    #11a — the DJ filter is THREE states now, not seven
    
    Settled by PLN: "sunset the dimorange and just have red in lows green in highs
    for clarity" + "i agree on clarity > resolution". The old ramp spent four of its
    seven steps on dim-amber and mid shades either side of centre, so the row read
    as a wash of oranges at a glance and the one thing you actually need — WHICH WAY
    is this filter cutting — was the hardest bit to see. Now: bright red (LPF, lows)
    / bright amber at the 61-67 detent band (bypass) / bright green (HPF, highs).
    All three are palette corners, the only states that read reliably on a dark
    stage. The centre band stays bright because djf 0.5 / CC 64 is BYPASS, not zero
    — djf 0.05 is a ~26 Hz low-pass, i.e. silence, which is the #48 footgun this
    colour exists to keep visible.
    
    #11b — "I See only two states, dim and not dim, at 0 and not 0 atm on the knobs"
    
    Also true, and embarrassing: `value_ramp` — his own verbatim six-step spec — was
    written, unit-tested, documented in the module header, and never once called.
    control_colour had exactly three outcomes for knobs (dim / full / flash), so a
    knob at 30% and one at 90% were the same colour. Wired in.
    
    The trade, stated because it should not regress silently: the ramp spends all
    three hues on VALUE, so hue no longer carries ROLE on rows A/B/C. That is the
    right way round — role is already fixed by POSITION (the lane convention),
    while a knob's setting has no other channel at all, since the pot's pointer is
    invisible on a dark stage. Keeping "untouched" as a colour was tried and
    abandoned: dim green would have meant both "rhythm, untouched" and "value ~3/4".
    That nuance belongs in the HUD, which has unlimited colours. Unbound controls
    still go DARK, which is the distinction that actually matters.
    
    Module header rewritten to describe the convention that now exists, rather than
    the one it used to.
    
    TESTS: +24 in test_lcxl_colour.py (8 -> 32), suite 282 -> 306, all green.
    The old knob rule had NO test at all — which is how a never-called ramp survived
    being shipped. Now covered: the filter has exactly three states and no dim amber
    survives; the detent band is contiguous and symmetric; the three mutes are equal
    at rest AND when engaged; arming repaints all four members and nothing else; an
    omitted panic argument behaves as unarmed (the #55 "untouched means zero"
    lesson); the overlay beats both role and dark; and the chord is asserted NOT to
    be force-bound as fx.
    
    Live: lcxl-leds-watch restarted, board repainted.
    PLN (Algolia) authored Jul 29, 2026
    0b23f3d9
mock-lcxl.py 7.82 KB
EditWeb IDE
×

Replace mock-lcxl.py

Attach a file by drag & drop or click to upload


Cancel
A new branch will be created in your fork and a new merge request will be started.