docs(product): reshape S16 to Varispeed/Preserve pitch-engine modes (duration-preserving correction)
Per-zone toggle: Varispeed (classic, cheap) vs Preserve (duration-holding, WDL_SimplePitchShifter candidate — per-voice RT viability assessed: pre-warm settles onset latency + queue growth; quality basic; hand-rolled pure module held as upgrade). Pitch envelope rides either engine. Fork S16-F1: default mode is Daniel's call.
This commit is contained in:
+110
-13
@@ -7,12 +7,20 @@ the **product framing behind a scoped phase**. Its build roadmap lives in **PLAN
|
||||
*why* (the plugin-format reasoning, the bare-VST3-vs-JUCE assessment, the settled
|
||||
decision record).
|
||||
|
||||
Status: framed by product-designer (2026-07-26), **revised 2026-07-26 (r7)**. r7 records the
|
||||
Status: framed by product-designer (2026-07-26), **revised 2026-07-26 (r8)**. r8 records
|
||||
Daniel's **duration-preserving correction** (2026-07-26, verbatim: *"isn't that ratio stuff
|
||||
going to change the playback rate? I want duration-preserving repitching"*): the ratio path is
|
||||
**varispeed** (pitch/duration coupled), so S16 is reshaped from "pitch envelope only" into a
|
||||
**pitch-engine mode — Varispeed vs Preserve — plus the pitch envelope** riding either engine.
|
||||
The WDL verdict flips: `WDL_SimplePitchShifter` (duration-preserving OLA), previously dismissed
|
||||
as the wrong tool, is **now the Preserve-engine candidate** and got a real per-voice RT
|
||||
viability assessment. Two S16 forks flagged: **S16-F1** (engine default — lean Preserve) and
|
||||
**S16-F2** (Preserve implementation — WDL shifter first, hand-rolled pure module held). See the
|
||||
r8 Addendum in §4. r7 records the
|
||||
**sampling-modes engine directive** (Daniel, 2026-07-26): Trigger vs Gate play modes (Gate =
|
||||
AHDSR, Trigger = one-shot with %-length + fades), a modifiable start point in both, and an
|
||||
off-by-default AD pitch envelope — specced as **new Phase S points S15/S16**, with the WDL
|
||||
pitch surface swept and reported (sinc `WDL_Resampler` held as an optional quality upgrade;
|
||||
no formant-preserving/elastique in WDL). See the "sampling modes" Addendum in §4. r6 records the
|
||||
pitch surface swept and reported. See the "sampling modes" Addendum in §4. r6 records the
|
||||
**workflow-first reframe of S10** (Daniel, 2026-07-26): the editor's default face becomes a
|
||||
**capture browser + guided single-capture setup**, a fresh instance is **silent with a "pick
|
||||
a capture" empty state** (reversing the S4 first-sample auto-play), and multi-zone editing is
|
||||
@@ -777,6 +785,12 @@ partly on filename, fall back to keeping the filename and record that as shipped
|
||||
|
||||
### Addendum — sampling modes (Trigger/Gate) + pitch envelope (Daniel, 2026-07-26)
|
||||
|
||||
> **Superseded in part by the r8 Addendum below (2026-07-26).** Daniel's duration-preserving
|
||||
> correction reshaped S16 from "pitch envelope only" into a Varispeed/Preserve pitch-engine
|
||||
> mode, and **flipped this addendum's WDL verdict** — `WDL_SimplePitchShifter` (called the
|
||||
> "wrong tool" in item 3 below) is now the Preserve-engine candidate. Read this as the r7
|
||||
> point-in-time record; the r8 Addendum carries the current S16 shape.
|
||||
|
||||
Daniel directed a set of engine features for the sampler, specced as **new Phase S points
|
||||
S15 (Trigger vs Gate) and S16 (pitch envelope)**. **The feature set is settled** — recorded
|
||||
here per the doc's settled-decisions convention; PLAN.md §S15/S16 and CONTEXT.md §Sampling
|
||||
@@ -829,6 +843,81 @@ off by default. Explore using WDL pitch capabilities."*
|
||||
**lean per-zone only** (the single capture is already a one-zone map), flagged because it
|
||||
touches S10's single-capture setup surface.
|
||||
|
||||
### Addendum — duration-preserving correction: S16 becomes pitch-engine modes (Daniel, 2026-07-26, r8)
|
||||
|
||||
**Correction, verbatim (Daniel, 2026-07-26):** *"isn't that ratio stuff going to change the
|
||||
playback rate? I want duration-preserving repitching."* Daniel is right about the mechanics.
|
||||
The r7 S16 spec modulated pitch by biasing the per-frame read ratio (`readPos_ += ratio_`) —
|
||||
that is **varispeed**: pitch and duration are coupled (an octave up halves the note's
|
||||
duration). Daniel wants **duration-preserving** repitch (a transposed note keeps its length).
|
||||
This reshapes S16 and **flips the r7 WDL verdict** on `WDL_SimplePitchShifter`.
|
||||
|
||||
**What changed (the reframe, then the spec):**
|
||||
|
||||
1. **The reframe — this is a mode, not a replacement.** Both behaviors are musically
|
||||
legitimate, so the answer is not "swap varispeed for preserve" but **a per-zone/per-capture
|
||||
pitch-engine mode**:
|
||||
- **Varispeed** (current path, cheap, zero-latency) — pitch/duration coupled. The
|
||||
**classic sampler / RS5K** default; right for **drums / one-shots** (pitch-down-lengthens-
|
||||
the-hit is a feature there).
|
||||
- **Preserve** (duration-preserving) — a pitch shifter transposes the output while the read
|
||||
holds the source duration. Right for **tempo-locked loops and phrases** (a transposed loop
|
||||
still lines up to the bar) — which is what captured banks skew toward (project slices).
|
||||
The pitch envelope (r7's S16 body) then rides **either** engine: under Varispeed it biases
|
||||
the read ratio (as specced); under Preserve it biases the shifter's shift amount. So the
|
||||
envelope is preserved, re-homed onto the engine seam.
|
||||
|
||||
2. **Fork S16-F1 (Daniel's call): the default engine. Lean Preserve.** Argued honestly:
|
||||
Preserve because Daniel asked for it **unprompted** (reads as his expectation) and the
|
||||
capture workflow is **loop/phrase-heavy**; but Varispeed is the **classic-sampler
|
||||
expectation**, is **cheaper + zero-latency**, is **bit-identical to today's shipped feel**,
|
||||
and is what percussive one-shots want. Recommendation: **default Preserve, prominent cheap
|
||||
per-zone toggle to Varispeed.** Daniel's call.
|
||||
|
||||
3. **WDL verdict corrected — `WDL_SimplePitchShifter` is now the right category.** Under
|
||||
"duration-preserving is the requirement," r7's dismissal ("wrong tool, duration-preserving
|
||||
OLA") inverts: **duration-preserving is exactly what we need.** A real per-voice RT
|
||||
viability assessment (from `vendor/WDL/WDL/simple_pitchshift.h`):
|
||||
- **API:** push/pull block (`GetBuffer`/`BufferDone`/`GetSamples`); `set_shift(2^(semi/12))`
|
||||
for pitch with an **independent** `set_tempo(1.0)` duration knob — pitch and duration
|
||||
separately controllable, exactly Preserve.
|
||||
- **Per-voice:** modest memory (OLA ring ≈ window·srate ≈ a few KB/voice at the 50 ms
|
||||
quality-0 window). CPU cheap (O(length), a few mults + one OLA crossfade/frame, **no
|
||||
FFT**) → **N polyphonic voices each running one is feasible** in RT discipline.
|
||||
- **Costs owned:** (i) **onset latency** ~half-window (~25 ms @ 50 ms) — the load-bearing
|
||||
cost; pre-warm at voice-allocation, and it lands on sustained/loop material (Varispeed
|
||||
serves tight one-shots); (ii) **queue-growth** allocation in `BufferDone` — settled by a
|
||||
silence pre-warm so no `process`-thread allocation in steady state; (iii) **basic
|
||||
quality** (SimpleWindowed warble on big transpositions; `set_formant_shift` is an empty
|
||||
stub → no formant preservation) — acceptable for loops, replaceable by route (b).
|
||||
- **Fork S16-F2:** **route (a)** `WDL_SimplePitchShifter` (low-cost proof) vs **route (b)** a
|
||||
hand-rolled pure `pitch_shift` OLA/granular module (house pattern, CTest-testable, full
|
||||
control). **Lean (a) first, (b) held** as the quality/latency upgrade — same
|
||||
`PitchEngine::Preserve` contract behind the seam.
|
||||
|
||||
4. **Not proposed / restated ceilings.** `WDL_Resampler` is a *resampler* (couples duration) —
|
||||
a held **Varispeed-quality** upgrade, **not** a Preserve engine. **elastique is NOT
|
||||
available** (licensed zplane, not vendored). JUCE / rubberband / signalsmith are each a
|
||||
**new-dependency fork carrying full D-A weight** (bare-VST3-no-framework is the locked D-A
|
||||
choice) — **not proposed**.
|
||||
|
||||
5. **S15 interaction (cleaner under Preserve).** Trigger's **%-length** becomes **pitch-
|
||||
independent** under Preserve (wall-clock stable under transpose — cleaner than Varispeed,
|
||||
where transposing a Trigger also scales its length); Gate's **sustain loop** contract under
|
||||
Preserve is *loop the source read, shift the output* (loop points stay source-frame facts);
|
||||
the **start point** is engine-independent (a source-frame offset). Channel-agnostic for S7
|
||||
(the shifter is `set_nch`-aware; one instance per voice carries all channels).
|
||||
|
||||
6. **RT/CPU honesty.** Preserve is **meaningfully heavier** than varispeed — a per-voice DSP
|
||||
object with its own budget, pre-warm, and a possible **Preserve-mode-specific voice cap**
|
||||
(below the Varispeed cap) if per-voice cost demands it. Put in Verify: pre-warm → no
|
||||
`process` allocation; measure per-voice CPU + onset latency against the polyphony cap. Treat
|
||||
S16's Preserve-engine point as the phase's next real DSP spike, not a thin envelope add-on.
|
||||
|
||||
**Where the spec lives:** PLAN.md §S16 (reshaped to "pitch engine modes + pitch envelope",
|
||||
with forks S16-F1/F2 and the corrected WDL finding) and the S15 × S16 interaction note;
|
||||
CONTEXT.md §Pitch engine modes — Varispeed vs Preserve + the corrected WDL surface finding.
|
||||
|
||||
---
|
||||
|
||||
## Where this landed
|
||||
@@ -878,21 +967,29 @@ Post-DAW-test directives (2026-07-26; see the "product name + UX overhaul" Adden
|
||||
current drawing and adopt the L1 kit when it lands — not gated on Phase L.** Answers
|
||||
Daniel's "the VST is dogshit / temple os / does Cockos have a toolkit" (2026-07-26,
|
||||
post-S1–S6 DAW test).
|
||||
10. **Sampling modes + pitch envelope → engine features** (**S15** Trigger vs Gate,
|
||||
**S16** pitch envelope; see the "sampling modes" Addendum in §4). Gate = AHDSR held note
|
||||
(hold added to today's ADSR); Trigger = one-shot with %-length + fade-in/out, ignores
|
||||
note-off; both carry a modifiable start point; Gate keeps loop points. Pitch envelope =
|
||||
per-voice AD, off by default, hand-rolled over the existing per-frame ratio read (no WDL
|
||||
needed). WDL swept: `WDL_Resampler` (sinc) is a held optional repitch-quality upgrade;
|
||||
no formant-preserving/elastique in WDL. Forks S15-F1 (choke, held) / S15-F2 (param
|
||||
granularity, lean per-zone) flagged. Feature set settled.
|
||||
10. **Sampling modes + pitch engine → engine features** (**S15** Trigger vs Gate, **S16**
|
||||
pitch-engine modes + pitch envelope; see the "sampling modes" r7 + "duration-preserving"
|
||||
r8 Addenda in §4). Gate = AHDSR held note (hold added to today's ADSR); Trigger = one-shot
|
||||
with %-length + fade-in/out, ignores note-off; both carry a modifiable start point; Gate
|
||||
keeps loop points. **S16 reshaped (r8, Daniel's duration-preserving correction):** a per-
|
||||
zone **pitch-engine mode — Varispeed** (current, cheap, pitch/duration coupled — classic
|
||||
sampler, right for drums) **vs Preserve** (duration-preserving via a per-voice pitch
|
||||
shifter — right for tempo-locked loops/phrases). Pitch envelope = per-voice AD, off by
|
||||
default, riding either engine (biases `ratio_` under Varispeed, the shift amount under
|
||||
Preserve). WDL verdict corrected: **`WDL_SimplePitchShifter` is the Preserve-engine
|
||||
candidate** (duration-preserving OLA — RT-viable per-voice with pre-warm; the load-bearing
|
||||
cost is onset latency), `WDL_Resampler` (sinc) held as a Varispeed-quality upgrade only; no
|
||||
formant-preserving/elastique in WDL. Forks: **S16-F1** (engine default — lean Preserve,
|
||||
Daniel's call), **S16-F2** (Preserve impl — WDL shifter first / hand-rolled held), plus
|
||||
S15-F1 (choke, held) / S15-F2 (param granularity, lean per-zone). Feature set settled;
|
||||
the engine default is Daniel's fork.
|
||||
|
||||
**Authoritative from here:** **PLAN.md §Phase S** is the roadmap (S1–S6 the original
|
||||
dependency chain: spike → `Sample` fields → pure sampler core → Tier 0 → Tier 1 → embedded
|
||||
UI; then **S7** stereo, **S8** ingest, **S9** change-detection, **S10–S13** the ReaSampler
|
||||
9000 UX overhaul, **S15/S16** the Trigger-vs-Gate + pitch-envelope engine features);
|
||||
9000 UX overhaul, **S15/S16** the Trigger-vs-Gate + pitch-engine-modes engine features);
|
||||
**CONTEXT.md §Phase S** is the spec (seam-field semantics, scope contracts, the channel-mode
|
||||
/ ingest / bank-generation / sampling-mode / pitch-envelope contracts, the UX-overhaul spec,
|
||||
/ ingest / bank-generation / sampling-mode / pitch-engine contracts, the UX-overhaul spec,
|
||||
the product-name convention, the pure/shell split, the WDL finding, the must-verify
|
||||
SDK/bridge surfaces). This doc is the framing/decision record they point back to. The "no
|
||||
PLAN.md footprint" era is over.
|
||||
|
||||
Reference in New Issue
Block a user