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
  • master-out.py
Find file
BlameHistoryPermalink
  • PLN (Algolia)'s avatar
    feat(rig): one supervised surface tap, and an output that follows a preference list · 560e0e95
    Two pieces of the cockpit (#155, #41), both written the same way: resolve by
    IDENTITY every time, never cache an address.
    
    tools/bridge/surface.py — THE control-surface tap
    -------------------------------------------------
    Four tools each spawned their own aseqdump. This morning three of them were
    bound to ALSA client 24 (an Arturia KeyStep) instead of 20 (the Launch Control
    XL) because a replug had renumbered the clients, and each resolves its port
    once, at start. I "fixed" them by moving them to 20:0.
    
    Then PLN replugged again this evening and the numbers SWAPPED BACK:
    
        client 20: 'Arturia KeyStep 32'     <- was the LCXL
        client 24: 'Launch Control XL'      <- was the KeyStep
    
    So the two I fixed are wrong again and the one I called broken is now right.
    An address is a lease, not an identity. This module resolves "Launch Control
    XL" by name on EVERY respawn, so a replug costs one backoff interval instead of
    a silent session. First run against the live rig, with zero configuration:
    
        starting   port=None
        ok         port=24:0
    
    It also keeps the last value of every CC, and — the part that matters — records
    `seen: false` distinctly from `value: 0`. An untouched control is what silences
    a Tidal stream (a bare "^NN" with no value yields NO EVENTS), so a cockpit that
    rendered untouched-as-zero would hide the exact failure it exists to show.
    
    And it does not litter. Three smoke runs of this file each orphaned an
    aseqdump to pid 1 — precisely the mess PLN complained about this morning
    ("i see 4 aseqdumps... you had one job"). try/finally is not enough, because
    SIGKILL, OOM and `timeout` all skip it. PR_SET_PDEATHSIG puts the guarantee in
    the kernel: the tap dies with its parent however the parent dies.
    
    tools/master-out.py — Ardour Master follows a preference list
    -------------------------------------------------------------
    PLN: "can we control this tedious routing... ideally theres a tie breaking list
    of choice, from uhc to headphones to speakers". Ardour's master does not follow
    the PipeWire default sink, so every rig move means re-patching in qjackctl.
    
    Not "route to X" but "route to the best X actually present": UMC -> Headphones
    -> Speakers, matched as substrings of the node name so serial numbers and
    profile suffixes do not matter.
    
    Two properties make it safe to run mid-set:
      * IDEMPOTENT — computes wanted links, diffs against existing, applies only
        the delta. Already correct = zero pw-link calls = cannot glitch the audio.
        The naive unlink-then-relink is a guaranteed dropout even when nothing
        changed.
      * EXCLUSIVE BUT NARROW — removes Master links to other sinks (or plugging the
        UMC back in gives you doubled output), and touches nothing else. The limit
        is a predicate, `_is_ours`, not a good intention: apply() refuses to unlink
        any source that is not a Master output.
    
    Verified against KNOWN-BAD states, not just against agreement with reality —
    the whole lesson of this session. Ten checks: preference order at each of the
    three tiers, the "master stranded on headphones when the UMC appears" case
    (2 adds + 2 removes), idempotency (0 and 0), and the guard refusing both a
    foreign link in plan() and a foreign source handed straight to apply().
    All pass. Live dry-run reports "already correct — 0 changes".
    PLN (Algolia) authored Aug 14, 2026
    560e0e95
master-out.py 7.14 KB
EditWeb IDE
×

Replace master-out.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.