Commit a32c6d2c by PLN (Algolia)

feat(tools): set-coherence — 108 controls sit idle under his hand, and three of…

feat(tools): set-coherence — 108 controls sit idle under his hand, and three of them are systematic (#113)

PLN, 2026-07-31, having walked the OPAL set by eye:

  "when i went across set i saw some unmapped effects/button, a 'set coherence
   mapping review' and quick wins suggestions (bass octersub, lead slice, etc etc)
   could be good ways for you to fill-in suggest"

He was right, and the number is larger than "some": **108 idle slots across 117
declared orbits in the 13 setlist tracks.**

## Why this needed a tool rather than a read-through

Absence has no line number. The surface is a fixed grid — each orbit owns a known set
of cells (effect knob, maybe a second, gate button, maybe a second) — so a track that
declares d5 and never references ^33 leaves d5's knob inert for that track's whole
duration. You cannot grep for a control that isn't there; you can only compute it, by
subtracting what the track USES from what the orbit OWNS.

The tool reports both directions of the same coherence question, deliberately:
  IDLE     orbit owns a slot the track never uses   -> unused affordance (new)
  STARVED  track drives more controls than it owns  -> already covered by PV008
                                                       and the FIXME(#54)/(#94) markers
Fixing one can create the other, which is why they belong in one report.

## Three systematic findings, not a flat list

1. **The drums' own effect knobs are the deadest controls on the board.**
   d2.fx (B2 ^30) idle in 12/13 tracks, d1.fx (B1 ^29) 9/13, d3.fx (B3 ^31) 8/13.
   d1-d3 own exactly ONE per-orbit knob each — their row-C cells are the family
   filters — so this is not a spare slot going unused, it is their only knob. The
   KICK has no per-orbit effect knob bound anywhere in the entire set.

2. **Row A (d9-d12 effects, A5-A8) is ~90% idle.** d11 6/6, d12 5/5, d10 6/7, d9 5/8.
   That is #98's proposal (pool A5-A8 in order of appearance) with its supporting
   numbers: four knobs almost never used, on exactly the orbits that overflow.

3. **d6 has gates but NO knobs, 5 of 5 tracks.** Fallout from the d7->d6
   consolidation: ^58/^90 came along, ^34/^54 did not. And vague_de_crime's d6
   references nothing at all — no knob, no gate.

## Suggestions are grounded, not invented

Measured what PLN actually binds to a per-orbit effect knob across the set — perc
legato(4); bass lpf(3), gain(1), bandf(1); lead width(2), modIndex, filterRange,
cutoff, resonance; kick nothing — and the suggestion table echoes that idiom before
extending it (memory feedback_self_reference_rhythm_study: extract the grammar from
his own tracks rather than importing generic Tidal advice).

Worth recording because it inverts the ask: his two named wants sit OUTSIDE his
measured idiom. He binds lpf to bass knobs and never octersub; slice lives on his
buttons, not his knobs. So "bass octersub, lead slice" are additions he is reaching
for, not corrections of drift — and saying so is more useful than pretending the
corpus already implied them.

## Deliberately not applied
Every suggestion changes how a track sounds and he is rehearsing on these files this
weekend. Output is docs/2026-07-31-set-coherence.md to skim and cherry-pick. The tool
also never suggests binding CC 77-84 / 13-16 (Ardour track gains), 93 (gPanic), or the
family cells 49/50/51 + 73/74/75.

Also confirms the #94 remap held: "borrowed" CCs — ones an orbit drives without owning
— are 0 or 1 across every track in the set.
parent af868ccd
# Set coherence — controls idle under your hand
PLN 2026-07-31: *"when i went across set i saw some unmapped effects/button, a 'set
coherence mapping review' and quick wins suggestions (bass octersub, lead slice, etc
etc) could be good ways for you to fill-in suggest"*
**108 idle slots across 117 declared orbits, 13 tracks.** Nothing here has been
applied — every line below changes how a track sounds, and you're rehearsing on these
files. Skim and cherry-pick.
## The three systematic findings (not a flat list)
### 1. The drums' own effect knobs are the deadest controls on the board
```
d2.fx B2 ^30 idle in 12 of 13 tracks 92%
d1.fx B1 ^29 idle in 9 of 13 69%
d3.fx B3 ^31 idle in 8 of 13 62%
```
The whole left third of row B does nothing in most of the set. d1/d2/d3 own exactly
ONE per-orbit knob each (their row-C cells are the family filters), so this isn't a
spare — it's their only knob, unused. **The kick has no per-orbit effect knob bound
anywhere in the entire set.**
### 2. Row A (d9-d12 effects, A5-A8) is ~90% idle — this is #98 confirmed
```
d11.fx A7 ^19 6/6 100% d12.fx A8 ^20 5/5 100%
d10.fx A6 ^18 6/7 86% d9.fx A5 ^17 5/8 62%
```
#98 proposes pooling A5-A8 in order of appearance. These numbers are the argument for
it: four knobs, almost never used, while d9-d12 are exactly the orbits that overflow.
### 3. d6 has gates but NO knobs, in 5 of 5 tracks
```
d6.fx B6 ^34 5/5 100% d6.fx2 C6 ^54 5/5 100%
```
Consequence of your d7→d6 consolidation: the buttons came along (^58/^90 are bound in
do_it_right, perfect, you_my_sunshine, desire) but neither knob did.
**`vague_de_crime`'s d6 references nothing at all** — no knob, no gate.
## Grounding for the suggestions
Measured: what you *already* bind to a per-orbit effect knob, set-wide.
```
perc legato(4)
bass lpf(3), gain(1), bandf(1)
lead width(2), modIndex(1), filterRange(1), cutoff(1), resonance(1)
kick nothing
```
So suggestions echo your idiom first. Note your two named wants sit outside it: you
bind **lpf** to bass knobs, never **octersub**; and **slice** currently lives on
buttons, not knobs. Both are additions, not corrections.
"borrowed" in the table = CCs an orbit drives that it doesn't own. It's 0 or 1
everywhere — the #94 remap held.
---
set-coherence — controls that sit idle under your hand, per track
track orbits idle borrowed idle slots
--------------------------------------------------------------------------------------------
bombe_dj 8 2 1 d2.fx d8.gate2
wap 8 7 1 d1.fx d2.fx d4.fx d4.fx2 d5.fx2 d7.fx d7.fx2
do_it_right 9 8 1 d2.fx d3.fx d3.gate d4.gate d6.fx d6.fx2 d10.fx d12.fx
take_5_drops 10 7 0 d1.fx d2.fx d4.gate d7.gate d10.fx d11.fx d12.fx
piment_bresilien 8 4 0 d1.fx d2.fx d7.fx2 d7.gate
perfect 12 13 0 d1.fx d2.fx d3.fx d6.fx d6.fx2 d6.gate d7.gate2 d8.fx2 d8.gate2 d9.fx d10.fx d11.fx d12.fx
gimme_acid 10 9 0 d1.fx d2.fx d3.fx d5.gate2 d8.fx2 d9.fx d10.fx d11.fx d12.fx
vague_de_crime 9 14 0 d1.fx d2.fx d2.gate d3.fx d4.gate d5.fx d5.fx2 d5.gate2 d6.fx d6.fx2 d6.gate d6.gate2 d7.fx2 d10.fx
mafia_sans_serif 7 6 0 d1.fx d2.fx d3.fx d4.gate d5.fx2 d7.gate
you_my_sunshine 9 12 0 d1.fx d2.fx d3.fx d4.gate2 d5.fx d5.fx2 d5.gate2 d6.fx d6.fx2 d8.gate d9.fx d11.fx
desire 9 7 1 d4.fx2 d4.gate d4.gate2 d5.fx2 d5.gate d6.fx d6.fx2
the_revolution_will_be_sampled 11 10 0 d2.fx d3.fx d3.gate d5.fx d5.fx2 d5.gate d9.fx d10.fx d11.fx d12.fx
electric_hammer 7 9 0 d1.fx d2.fx d2.gate d3.fx d3.gate d4.gate2 d5.fx2 d9.fx d11.fx
TOTAL: 117 declared orbits across 13 tracks, 108 idle slots
idle slots by role: lead=36, perc=25, fx=22, bass=11, kick=9, breaks=5
### bombe_dj
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d8 (breaks) owns [fx=^36, fx2=^56, gate=^60, gate2=^92] — IDLE: gate2=^92
+ slice / chop button. re-cut the break live
+ loopAt + rev button. stretch or reverse
+ legato knob. shorten the break's tails
### wap
d1 (kick) owns [fx=^29, gate=^41] — IDLE: fx=^29
+ lpf / hpf knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set
+ octersub knob. sub weight under the kick, same family as the bass idea
+ mask / struct button. drop or stutter the 4-on-the-floor
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d4 (bass) owns [fx=^32, fx2=^52, gate=^44, gate2=^76] — IDLE: fx=^32, fx2=^52
+ octersub knob. YOU NAMED THIS. note you currently bind lpf(3) here, never octersub
+ lpf knob. your existing bass idiom, if the slot is free on a track that lacks it
+ crush knob. grit on the low end
d5 (lead) owns [fx=^33, fx2=^53, gate=^57, gate2=^89] — IDLE: fx2=^53
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d7 (lead) owns [fx=^35, fx2=^55, gate=^59, gate2=^91] — IDLE: fx=^35, fx2=^55
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
### do_it_right
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d3 (perc) owns [fx=^31, gate=^43] — IDLE: fx=^31, gate=^43
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d4 (bass) owns [fx=^32, fx2=^52, gate=^44, gate2=^76] — IDLE: gate=^44
+ octersub knob. YOU NAMED THIS. note you currently bind lpf(3) here, never octersub
+ lpf knob. your existing bass idiom, if the slot is free on a track that lacks it
+ crush knob. grit on the low end
d6 (lead) owns [fx=^34, fx2=^54, gate=^58, gate2=^90] — IDLE: fx=^34, fx2=^54
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d10 (fx) owns [fx=^18] — IDLE: fx=^18
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d12 (fx) owns [fx=^20] — IDLE: fx=^20
also drives non-own CCs: ^31
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
### take_5_drops
d1 (kick) owns [fx=^29, gate=^41] — IDLE: fx=^29
+ lpf / hpf knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set
+ octersub knob. sub weight under the kick, same family as the bass idea
+ mask / struct button. drop or stutter the 4-on-the-floor
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d4 (bass) owns [fx=^32, fx2=^52, gate=^44, gate2=^76] — IDLE: gate=^44
+ octersub knob. YOU NAMED THIS. note you currently bind lpf(3) here, never octersub
+ lpf knob. your existing bass idiom, if the slot is free on a track that lacks it
+ crush knob. grit on the low end
d7 (lead) owns [fx=^35, fx2=^55, gate=^59, gate2=^91] — IDLE: gate=^59
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d10 (fx) owns [fx=^18] — IDLE: fx=^18
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d11 (fx) owns [fx=^19] — IDLE: fx=^19
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d12 (fx) owns [fx=^20] — IDLE: fx=^20
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
### piment_bresilien
d1 (kick) owns [fx=^29, gate=^41] — IDLE: fx=^29
+ lpf / hpf knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set
+ octersub knob. sub weight under the kick, same family as the bass idea
+ mask / struct button. drop or stutter the 4-on-the-floor
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d7 (lead) owns [fx=^35, fx2=^55, gate=^59, gate2=^91] — IDLE: fx2=^55, gate=^59
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
### perfect
d1 (kick) owns [fx=^29, gate=^41] — IDLE: fx=^29
+ lpf / hpf knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set
+ octersub knob. sub weight under the kick, same family as the bass idea
+ mask / struct button. drop or stutter the 4-on-the-floor
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d3 (perc) owns [fx=^31, gate=^43] — IDLE: fx=^31
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d6 (lead) owns [fx=^34, fx2=^54, gate=^58, gate2=^90] — IDLE: fx=^34, fx2=^54, gate=^58
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d7 (lead) owns [fx=^35, fx2=^55, gate=^59, gate2=^91] — IDLE: gate2=^91
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d8 (breaks) owns [fx=^36, fx2=^56, gate=^60, gate2=^92] — IDLE: fx2=^56, gate2=^92
+ slice / chop button. re-cut the break live
+ loopAt + rev button. stretch or reverse
+ legato knob. shorten the break's tails
d9 (fx) owns [fx=^17] — IDLE: fx=^17
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d10 (fx) owns [fx=^18] — IDLE: fx=^18
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d11 (fx) owns [fx=^19] — IDLE: fx=^19
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d12 (fx) owns [fx=^20] — IDLE: fx=^20
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
### gimme_acid
d1 (kick) owns [fx=^29, gate=^41] — IDLE: fx=^29
+ lpf / hpf knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set
+ octersub knob. sub weight under the kick, same family as the bass idea
+ mask / struct button. drop or stutter the 4-on-the-floor
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d3 (perc) owns [fx=^31, gate=^43] — IDLE: fx=^31
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d5 (lead) owns [fx=^33, fx2=^53, gate=^57, gate2=^89] — IDLE: gate2=^89
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d8 (breaks) owns [fx=^36, fx2=^56, gate=^60, gate2=^92] — IDLE: fx2=^56
+ slice / chop button. re-cut the break live
+ loopAt + rev button. stretch or reverse
+ legato knob. shorten the break's tails
d9 (fx) owns [fx=^17] — IDLE: fx=^17
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d10 (fx) owns [fx=^18] — IDLE: fx=^18
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d11 (fx) owns [fx=^19] — IDLE: fx=^19
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d12 (fx) owns [fx=^20] — IDLE: fx=^20
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
### vague_de_crime
d1 (kick) owns [fx=^29, gate=^41] — IDLE: fx=^29
+ lpf / hpf knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set
+ octersub knob. sub weight under the kick, same family as the bass idea
+ mask / struct button. drop or stutter the 4-on-the-floor
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30, gate=^42
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d3 (perc) owns [fx=^31, gate=^43] — IDLE: fx=^31
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d4 (bass) owns [fx=^32, fx2=^52, gate=^44, gate2=^76] — IDLE: gate=^44
+ octersub knob. YOU NAMED THIS. note you currently bind lpf(3) here, never octersub
+ lpf knob. your existing bass idiom, if the slot is free on a track that lacks it
+ crush knob. grit on the low end
d5 (lead) owns [fx=^33, fx2=^53, gate=^57, gate2=^89] — IDLE: fx=^33, fx2=^53, gate2=^89
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d6 (lead) owns [fx=^34, fx2=^54, gate=^58, gate2=^90] — IDLE: fx=^34, fx2=^54, gate=^58, gate2=^90
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d7 (lead) owns [fx=^35, fx2=^55, gate=^59, gate2=^91] — IDLE: fx2=^55
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d10 (fx) owns [fx=^18] — IDLE: fx=^18
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
### mafia_sans_serif
d1 (kick) owns [fx=^29, gate=^41] — IDLE: fx=^29
+ lpf / hpf knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set
+ octersub knob. sub weight under the kick, same family as the bass idea
+ mask / struct button. drop or stutter the 4-on-the-floor
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d3 (perc) owns [fx=^31, gate=^43] — IDLE: fx=^31
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d4 (bass) owns [fx=^32, fx2=^52, gate=^44, gate2=^76] — IDLE: gate=^44
+ octersub knob. YOU NAMED THIS. note you currently bind lpf(3) here, never octersub
+ lpf knob. your existing bass idiom, if the slot is free on a track that lacks it
+ crush knob. grit on the low end
d5 (lead) owns [fx=^33, fx2=^53, gate=^57, gate2=^89] — IDLE: fx2=^53
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d7 (lead) owns [fx=^35, fx2=^55, gate=^59, gate2=^91] — IDLE: gate=^59
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
### you_my_sunshine
d1 (kick) owns [fx=^29, gate=^41] — IDLE: fx=^29
+ lpf / hpf knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set
+ octersub knob. sub weight under the kick, same family as the bass idea
+ mask / struct button. drop or stutter the 4-on-the-floor
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d3 (perc) owns [fx=^31, gate=^43] — IDLE: fx=^31
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d4 (bass) owns [fx=^32, fx2=^52, gate=^44, gate2=^76] — IDLE: gate2=^76
+ octersub knob. YOU NAMED THIS. note you currently bind lpf(3) here, never octersub
+ lpf knob. your existing bass idiom, if the slot is free on a track that lacks it
+ crush knob. grit on the low end
d5 (lead) owns [fx=^33, fx2=^53, gate=^57, gate2=^89] — IDLE: fx=^33, fx2=^53, gate2=^89
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d6 (lead) owns [fx=^34, fx2=^54, gate=^58, gate2=^90] — IDLE: fx=^34, fx2=^54
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d8 (breaks) owns [fx=^36, fx2=^56, gate=^60, gate2=^92] — IDLE: gate=^60
+ slice / chop button. re-cut the break live
+ loopAt + rev button. stretch or reverse
+ legato knob. shorten the break's tails
d9 (fx) owns [fx=^17] — IDLE: fx=^17
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d11 (fx) owns [fx=^19] — IDLE: fx=^19
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
### desire
d4 (bass) owns [fx=^32, fx2=^52, gate=^44, gate2=^76] — IDLE: fx2=^52, gate=^44, gate2=^76
also drives non-own CCs: ^45
+ octersub knob. YOU NAMED THIS. note you currently bind lpf(3) here, never octersub
+ lpf knob. your existing bass idiom, if the slot is free on a track that lacks it
+ crush knob. grit on the low end
d5 (lead) owns [fx=^33, fx2=^53, gate=^57, gate2=^89] — IDLE: fx2=^53, gate=^57
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d6 (lead) owns [fx=^34, fx2=^54, gate=^58, gate2=^90] — IDLE: fx=^34, fx2=^54
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
### the_revolution_will_be_sampled
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d3 (perc) owns [fx=^31, gate=^43] — IDLE: fx=^31, gate=^43
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d5 (lead) owns [fx=^33, fx2=^53, gate=^57, gate2=^89] — IDLE: fx=^33, fx2=^53, gate=^57
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d9 (fx) owns [fx=^17] — IDLE: fx=^17
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d10 (fx) owns [fx=^18] — IDLE: fx=^18
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d11 (fx) owns [fx=^19] — IDLE: fx=^19
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d12 (fx) owns [fx=^20] — IDLE: fx=^20
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
### electric_hammer
d1 (kick) owns [fx=^29, gate=^41] — IDLE: fx=^29
+ lpf / hpf knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set
+ octersub knob. sub weight under the kick, same family as the bass idea
+ mask / struct button. drop or stutter the 4-on-the-floor
d2 (perc) owns [fx=^30, gate=^42] — IDLE: fx=^30, gate=^42
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d3 (perc) owns [fx=^31, gate=^43] — IDLE: fx=^31, gate=^43
+ legato knob. YOUR idiom — the only thing you bind to a perc knob (4x)
+ pan / width knob. widen the hats without touching level
+ ply button. double-time on a push
d4 (bass) owns [fx=^32, fx2=^52, gate=^44, gate2=^76] — IDLE: gate2=^76
+ octersub knob. YOU NAMED THIS. note you currently bind lpf(3) here, never octersub
+ lpf knob. your existing bass idiom, if the slot is free on a track that lacks it
+ crush knob. grit on the low end
d5 (lead) owns [fx=^33, fx2=^53, gate=^57, gate2=^89] — IDLE: fx2=^53
+ slice button. YOU NAMED THIS. re-order the melodic phrase
+ cutoff / resonance knob. YOUR idiom — what you already bind to lead knobs
+ width / modIndex knob. also yours; width appears 2x
d9 (fx) owns [fx=^17] — IDLE: fx=^17
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
d11 (fx) owns [fx=^19] — IDLE: fx=^19
+ room / sz knob. open the space
+ delay + delayfb button. throw a repeat
+ gain swell knob. A-row slots are ~90% idle — see #98 on pooling these
#!/usr/bin/env python3
"""set-coherence — which controls under PLN's hands do NOTHING on this track?
WHY THIS EXISTS
---------------
PLN, 2026-07-31, having walked the OPAL set by eye:
*"when i went across set i saw some unmapped effects/button, a 'set coherence
mapping review' and quick wins suggestions (bass octersub, lead slice, etc etc)
could be good ways for you to fill-in suggest"*
The surface is a fixed grid: every orbit owns a known set of cells (an effect knob,
maybe a second one, a gate button, maybe a second one). A track that declares `d5`
but never references `^33` leaves d5's effect knob **inert for the length of that
track** — a knob sitting under his hand doing nothing. That is invisible from the
code, because absence has no line number.
This tool reports the two OPPOSITE errors together, which is the point:
IDLE the orbit owns a slot and the track never uses it -> an unused affordance
STARVED the track drives more controls than the orbit owns -> #54/#98 territory
They are the same coherence question from either side, and fixing one can create the
other. pvlint's PV008 and the FIXME(#54)/FIXME(#94) markers already cover STARVED;
nothing covered IDLE until now.
WHAT IT DOES NOT DO
-------------------
Write to a `.tidal`. Every suggestion here changes how a track SOUNDS, and PLN is
rehearsing on these files. Output is a report to skim and cherry-pick.
It also never suggests binding CC 77-84 or 13-16 (Ardour owns those track gains —
seeding one is the CC77-goes-to-silence footgun) or 93 (gPanic), or the family cells
49/50/51 + 73/74/75, which belong to gF/gM and not to any single orbit.
USAGE
python3 tools/set-coherence.py # the OPAL set, summary
python3 tools/set-coherence.py --detail # per-orbit breakdown
python3 tools/set-coherence.py --suggest # + quick-win candidates
python3 tools/set-coherence.py --md report.md # write the full report
python3 tools/set-coherence.py <track.tidal> ... # specific tracks
"""
from __future__ import annotations
import argparse
import collections
import pathlib
import re
import sys
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
import lcxl_grid as grid # noqa: E402
from pvlint.core import Track, strip_comment # noqa: E402
ROOT = pathlib.Path(__file__).resolve().parent.parent
SETLIST = ROOT / "armada" / "setlist_opal2026.txt"
CC_REF = re.compile(r'"\^(\d+)"')
# Slots an orbit can own that are actually PLAYABLE by hand during a track.
# `level` is excluded on purpose: rows A1-A4 and D are MIDI-learned to Ardour
# (grid.ARDOUR_CCS), so a track referencing one is a conflict, not a gap.
PLAYABLE = ("fx", "fx2", "gate", "gate2")
# Role vocabulary. Grounded in what PLN already plays rather than invented — the
# corpus check below reports how often he uses each, so a suggestion can say
# "you do this in N other tracks" instead of "you could try".
ROLE_OF = {1: "kick", 2: "perc", 3: "perc", 8: "breaks", 4: "bass"}
# Suggestions are GROUNDED, not invented. Measured across the 13 setlist tracks,
# what PLN already binds to a per-orbit effect KNOB when he binds anything:
# perc legato(4)
# bass lpf(3), gain(1), bandf(1)
# lead width(2), modIndex(1), filterRange(1), cutoff(1), resonance(1)
# kick (nothing — B1 is unused in 9 of 13 tracks)
# So the first suggestion per role echoes his own idiom; the rest extend it, and
# the two he named himself ("bass octersub, lead slice") are marked. See memory
# feedback_self_reference_rhythm_study: extract the grammar from his own tracks
# rather than importing generic Tidal advice.
SUGGEST = {
"kick": [("lpf / hpf", "knob. B1 is idle in 9/13 — the kick has NO per-orbit knob anywhere in the set"),
("octersub", "knob. sub weight under the kick, same family as the bass idea"),
("mask / struct", "button. drop or stutter the 4-on-the-floor")],
"perc": [("legato", "knob. YOUR idiom — the only thing you bind to a perc knob (4x)"),
("pan / width", "knob. widen the hats without touching level"),
("ply", "button. double-time on a push")],
"bass": [("octersub", "knob. YOU NAMED THIS. note you currently bind lpf(3) here, never octersub"),
("lpf", "knob. your existing bass idiom, if the slot is free on a track that lacks it"),
("crush", "knob. grit on the low end")],
"breaks": [("slice / chop", "button. re-cut the break live"),
("loopAt + rev", "button. stretch or reverse"),
("legato", "knob. shorten the break's tails")],
"lead": [("slice", "button. YOU NAMED THIS. re-order the melodic phrase"),
("cutoff / resonance", "knob. YOUR idiom — what you already bind to lead knobs"),
("width / modIndex", "knob. also yours; width appears 2x")],
"fx": [("room / sz", "knob. open the space"),
("delay + delayfb", "button. throw a repeat"),
("gain swell", "knob. A-row slots are ~90% idle — see #98 on pooling these")],
}
def role(orbit: int) -> str:
if orbit in ROLE_OF:
return ROLE_OF[orbit]
return "lead" if orbit <= 8 else "fx"
def setlist_tracks() -> list[pathlib.Path]:
if not SETLIST.exists():
raise SystemExit(f"set-coherence: no setlist at {SETLIST}")
return [ROOT / ln.split("#")[0].strip()
for ln in SETLIST.read_text().splitlines() if ln.split("#")[0].strip()]
def analyse(path: pathlib.Path) -> dict:
"""Per orbit: which owned slots are used, which are idle, what is borrowed."""
text = path.read_text()
out: dict[int, dict] = {}
for orb in Track(path=str(path), text=text).orbits():
code = "\n".join(strip_comment(ln) for ln in orb.lines)
used = {int(m.group(1)) for m in CC_REF.finditer(code)}
home = grid.orbit_home(orb.number)
owned = {k: home[k] for k in PLAYABLE if k in home}
idle = {k: cc for k, cc in owned.items() if cc not in used}
# Referenced CCs that are neither this orbit's own nor a family/Ardour cell.
foreign = sorted(used - set(owned.values())
- grid.FAMILY_CCS - grid.ARDOUR_CCS)
prev = out.get(orb.number)
if prev: # re-declared orbit: merge, keep the union
prev["used"] |= used
prev["idle"] = {k: cc for k, cc in owned.items()
if cc not in prev["used"]}
prev["foreign"] = sorted(set(prev["foreign"]) | set(foreign))
continue
out[orb.number] = {"used": used, "owned": owned,
"idle": idle, "foreign": foreign}
return out
def main() -> int:
ap = argparse.ArgumentParser(prog="set-coherence")
ap.add_argument("--detail", action="store_true")
ap.add_argument("--suggest", action="store_true")
ap.add_argument("--md", type=pathlib.Path)
ap.add_argument("tracks", nargs="*", type=pathlib.Path)
a = ap.parse_args()
tracks = ([p if p.is_absolute() else ROOT / p for p in a.tracks]
or setlist_tracks())
L: list[str] = []
def say(s: str = "") -> None:
L.append(s)
print(s)
say("set-coherence — controls that sit idle under your hand, per track")
say()
say(f"{'track':32} {'orbits':>6} {'idle':>5} {'borrowed':>9} idle slots")
say("-" * 92)
tot_idle = tot_orb = 0
idle_by_role: collections.Counter[str] = collections.Counter()
per_track: dict[str, dict] = {}
for p in tracks:
if not p.exists():
say(f" {p}: MISSING")
continue
info = analyse(p)
per_track[p.stem] = info
n_idle = sum(len(v["idle"]) for v in info.values())
n_borrow = sum(len(v["foreign"]) for v in info.values())
tot_idle += n_idle
tot_orb += len(info)
bits = []
for n in sorted(info):
for k, cc in sorted(info[n]["idle"].items()):
bits.append(f"d{n}.{k}")
idle_by_role[role(n)] += 1
say(f"{p.stem:32} {len(info):6} {n_idle:5} {n_borrow:9} {' '.join(bits)}")
say()
say(f"TOTAL: {tot_orb} declared orbits across {len(per_track)} tracks, "
f"{tot_idle} idle slots")
say()
say("idle slots by role: " + ", ".join(f"{r}={c}" for r, c
in idle_by_role.most_common()))
if a.detail or a.suggest:
for stem, info in per_track.items():
rows = [(n, v) for n, v in sorted(info.items()) if v["idle"]]
if not rows:
continue
say()
say(f"### {stem}")
for n, v in rows:
r = role(n)
owned = ", ".join(f"{k}=^{cc}" for k, cc in sorted(v["owned"].items()))
idle = ", ".join(f"{k}=^{cc}" for k, cc in sorted(v["idle"].items()))
say(f" d{n} ({r}) owns [{owned}] — IDLE: {idle}")
if v["foreign"]:
say(f" also drives non-own CCs: "
f"{', '.join('^'+str(c) for c in v['foreign'])}")
if a.suggest:
for name, why in SUGGEST[r]:
say(f" + {name:20} {why}")
if a.md:
a.md.write_text("\n".join(L) + "\n")
print(f"\nwrote {a.md}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment