Commit 1ae6f369 by PLN (Algolia)

docs(gig): riser audit of the OPAL 2026 set — only 2 of 12 tracks have one

Task #33 asked "does every OPAL track have a riser, and are they harmonized?".
Answering it needed the setlist (backlog.md § OPAL 2026, 12 tracks with BPMs)
resolved to actual .tidal files, then every d10 / riser / sweep / impact hit read
in context. Result is worse than expected and worth having in writing before
rehearsal rather than discovering it on stage.

Findings:
- Only 2 of 12 tracks have a riser: Take Five Drops and Vague de CRIME.
- 5 tracks have d10 completely free (Bombe, WAP, Mafia, Sunshine, Desire) — a
  riser drops straight in.
- 4 tracks have d10 already occupied by non-riser material (Piment's "Menace",
  Perfect's euclidian arps, Gimme Acid's "GIMME THAT", Revolution's sweet
  trigger) — those need a different stream, not a d10 paste.
- Track 3, "Am i Doing it Right?", has NO .tidal file matching any slug I tried.
  Per the repo convention it must exist under a name I didn't guess. Flagged as
  an open question — the set is incomplete until it's found.

The two existing risers disagree with each other, which is the actual #33 problem:
Take Five sleeps 24 cycles and builds over the final 8 (a build-up INTO a drop),
while Vague de CRIME fires on cycle 1 of 8 at the sample's natural length (an
accent OUT of a downbeat). Same folder, opposite musical function — exactly the
muscle-memory hazard when you're moving track to track mid-set. Also caught: Take
Five's d10 is the only stream in the whole set with no gF/gM wrapper, so global
filters and mutes cannot touch it — reach for gF1 during that build and everything
ducks except the riser.

Proposed one house helper, `gRiser period build`, derived verbatim from PLN's own
working Take Five block rather than invented. loopAt is the load-bearing trick: it
makes the riser's length independent of the sample's natural length, so one helper
lands correctly from 120 to 166 BPM.

Validated the proposal two ways before recommending it, per build-the-katana-first:
- compiles clean against the real tidal-1.9.5 (ghc -fno-code, no stream started);
- the generated mask is BIT-IDENTICAL to the hand-written "<f!24 t!8>" — queryArc
  over cycles 0-32 gives exactly 24 False then 8 True for both, compared
  elementwise, mine == theirs -> True. It's a faithful refactor, not a lookalike.

Deliberately NOT committed to BootTidal.hs and NOT applied to any track: what the
grep cannot answer is whether stock gain 0.8 / room 0.4 sits right under a 166 BPM
liquid dnb track. That's ears. Suggested trial order is Desire (the FINALE, where a
missing build costs most), then Mafia and Sunshine (the set's peak).

Ties to #13: with default-xfade dN now baked in (98d073f2), every track launch
already blends over 4 cycles. A riser is the musical counterpart to that structural
crossfade — the xfade stops the seam being abrupt, the riser tells the room a
change is coming. Together they're what turn "ctrl+enter and pray" into a transition.
parent 98d073f2
# Riser audit — OPAL 2026 set (task #33)
**Date:** 2026-07-25 (J-10) · **Scope:** the 12 tracks named in `backlog.md` § OPAL 2026
· **Method:** static analysis of each track's `.tidal` (grep for `d10` + riser/sweep/impact
vocabulary), then reading every hit in context. No ear-validation — this is the *structural*
pass. Which risers actually sound good together is PLN's call.
## The headline
**Only 2 of 12 tracks have a riser.** Five more have `d10` entirely free; four have `d10`
occupied by non-riser material, so they'd need a different stream.
| # | Track | BPM | File | `d10` state | Riser? |
|---|---|---|---|---|---|
| 1 | Ceci n'est pas Une Bombe | 124 | `live/midi/nova/techno/bombe_dj.tidal` | free | ❌ |
| 2 | WAP | 133 | `live/midi/nova/dnb/wap.tidal` | free | ❌ |
| 3 | Am i Doing it Right? | 89* | **file not located** | — | ❓ |
| 4 | Take Five Drops | 124 | `live/midi/nova/lounge/take_5_drops.tidal` | riser | ✅ |
| 5 | Piment Brésilien | 124 | `live/collab/raph/piment_bresilien.tidal` | "Menace" | ❌ |
| 6 | Perfect <3 | 128 | `live/midi/nova/remix/perfect.tidal` | "Euclidian mid arps" | ❌ |
| 7 | Gimme Acid | 160 | `live/midi/nova/acid/gimme_acid.tidal` | "GIMME THAT" | ❌ |
| 8 | Vague de CRIME | 120 | `live/techno/vague_de_crime.tidal` | riser | ✅ |
| 9 | Mafia | 160 | `live/collab/raph/mafia_sans_serif.tidal` | free | ❌ |
| 10 | Sunshine | 166 | `live/midi/nova/dnb/liquid/you_my_sunshine.tidal` | free | ❌ |
| 11 | Desire (FINALE) | 129 | `live/collab/raph/desire.tidal` | free | ❌ |
| 12 | Revolution (encore) | — | `live/midi/nova/jazz/the_revolution_will_be_sampled.tidal` | "SUGAR KEY SWEET TRIGGER" | ❌ |
Bonus: `live/midi/nova/techno/electric_hammer.tidal` ("le shifteur marteau", floated as the
closer) also has no riser.
> **Open question for PLN:** track 3, *"Am i Doing it Right?"*, has no `.tidal` matching any
> obvious slug. Per the repo convention ("one track = one standalone `.tidal`, it always
> exists") it should be findable — it's probably filed under a name I didn't guess. Needed
> before the set is complete.
## The two existing idioms (they disagree)
**A — Take Five Drops, the "fair riser".** The considered one:
```haskell
d10 -- A FAIR RISER
$ mask "<f!24 t!8>" -- sleeps 24 cycles, fires on the last 8 → a 32-cycle phrase
$ loopAt 8 -- stretches the sample to fill exactly that 8-cycle window
$ "risers"
# cut 10 -- own cut group: a new riser chokes the old one
# room 0.4 # dry 1.4
# octersubsub 0.5
# gain 0.8
```
32 cycles = 8 bars, with the build over the final 2. `loopAt 8` is the load-bearing trick:
it makes the riser's *length* independent of the sample's natural length, so the same
sample lands correctly at 120 and at 166 BPM. That's what makes it reusable across the set.
**B — Vague de CRIME, the terse one:**
```haskell
d10 $ gF1 $ gM1 $ mask "<t f!7>" $ "risers:8"
```
Fires on cycle **1 of 8** and plays at the sample's natural length. Note it builds *out of*
the downbeat rather than *into* one — an accent, not a build-up. Different musical function
despite the same folder.
### Two inconsistencies worth fixing
1. **Idiom A is unwrapped.** Take Five's `d10` has no `gF`/`gM` — it is the only stream in
the set that the global filters and mutes cannot touch. If you reach for gF1 during that
build, everything ducks except the riser. Idiom B wraps correctly (`gF1 $ gM1`).
2. **Phase disagreement** (fires-last-8 vs fires-first-1) means "the riser" means two
different things depending on which track you're in — exactly the muscle-memory hazard
#33 is about.
## Proposal — one house helper
Idiom A, parameterised and wrapped, so a riser is a one-liner per track. Derived verbatim
from PLN's own working Take Five block, not invented:
```haskell
-- RISER (#33) — house build-up. `gRiser period build` sleeps for
-- (period-build) cycles then stretches a riser across the final `build`,
-- so it lands identically at any BPM. Self-choking on cut group 10.
gRiser period build =
(# cut 10) . (# room 0.4) . (# dry 1.4) . (# octersubsub 0.5) . (# gain 0.8)
. loopAt (pure $ fromIntegral build)
. mask (slow (fromIntegral period) $ fastFromList
(replicate (period - build) False ++ replicate build True))
```
Per-track usage then collapses to:
```haskell
d10 $ gF1 $ gM1 $ gRiser 32 8 $ "risers:3" -- 8-bar phrase, build over the last 2
d10 $ gF1 $ gM1 $ gRiser 16 4 $ "risers:8" -- tighter, for the 160+ dnb tracks
```
**Status: type-checked and structurally verified, but NOT ear-validated, NOT committed.**
Verified two ways before proposing it, rather than asserted:
- Compiles clean against the real `tidal-1.9.5` install (`ghc -fno-code`, no stream started).
- The generated mask is **bit-identical to PLN's hand-written `<f!24 t!8>`**`queryArc`
over cycles 0–32 gives exactly 24 × `False` then 8 × `True` for both, compared elementwise:
`mine == theirs → True`. So `gRiser 32 8` is a faithful refactor of the Take Five block,
not a lookalike.
What's still unverified is everything musical: whether the stock `# gain 0.8 # room 0.4`
sits right under a 166 BPM liquid dnb track is a question for ears, not grep. Suggested
trial order:
Desire (the FINALE — the one place a missing build is most costly), then Mafia and Sunshine
(the two 160+ tracks that carry the set's peak).
`risers` folder has 24+ samples (`RPE - FX - Riser 1-12`, `RPE2 - FX - Riser 1-12`), several
BPM-tagged in the filename (`- 140`, `- 144`, `- 145`) — worth matching tag to track tempo
where the natural length matters, though `loopAt` largely removes the need.
## Recommendation
Risers are cheap insurance for the thing PLN actually flagged — transitions. With
default-xfade dN now baked in (#13, commit `98d073f`), every track *launch* already blends
over 4 cycles. A riser on `d10` is the **musical** counterpart to that **structural**
crossfade: the xfade stops the seam being abrupt, the riser tells the room a change is
coming. The two together are what turn "ctrl+enter and pray" into a transition.
Deferred to PLN: sign off `gRiser`, then it's ~10 one-line additions.
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